Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Управление объектами


Автор: Wapo 31.1.2005, 18:12
У меня возникла следуящая проблема. На форме очень много компонентов TEdit имеющие имена Edit1, Edit2 ... Edit N. При каждой загрузке формы нужно заполнить их. Писать вручную очень долго и не очень красиво. Как можно организовать обращение к компонентам в цикле.

Автор: Vyacheslav 31.1.2005, 18:57
Обратиться то в цикле можно, только как в цикле будешь изменять то, чем их заполнить надо smile

Автор: NiJazz 1.2.2005, 09:04
Код

void __fastcall TForm1::Button1Click(TObject *Sender)
{
   ShortString     clName = "TEdit";
   for (int i=0; i < ComponentCount-1; i++)
   {
       if (clName.operator AnsiString() == Components[i]->ClassName().operator AnsiString())
       {
           //Нужно привести Components[i] к TEdit и вызвать метод
       }
   }
}

Вопрос к знатокам: как привести к TEdit?

Автор: Vyacheslav 1.2.2005, 10:32
Ой какой кошмар . Пoпроще нельзя? Это же С++
Код

void __fastcall TForm1::Button1Click(TObject *Sender)
{
   for (int i  = ComponentCount; i--;)
  {
      if (TEdit* Edit = dymamic_cast<TEdit*>(Components[i]) )
      {
          Edit->...
      }
  }
}

Автор: Vyacheslav 1.2.2005, 10:46
Кстати. Зачем использовать явно слючевое слово operator .
Код

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  AnsiString  clName = "TEdit";
  for (int i=0; i < ComponentCount; i++)
  {
      if (clName == Components[i]->ClassName())
      {
            //Нужно привести Components[i] к TEdit и вызвать метод
            ((TEdit*)Components[i])->...
      }
  }
}

Автор: Adil' 1.2.2005, 10:53
Хочу дополнить - оба кода будут работать, только если у Edit'oв Owner'ом Form1 является (хотя обычно так и бывает
smile ).

З.Ы. Vyacheslav за что жы так над for'ом глумишься? Я даже с первого захода не понял как оно работает smile
З.З.Ы. dynamic_cast

Автор: NiJazz 1.2.2005, 12:12
Код

if (TEdit* Edit = dymamic_cast<TEdit*>(Components[i]) )

Это он будет приводить все компоненты подряд?
Код

if (clName == Components[i]->ClassName())

Тут он выдавал ошибку.

Автор: Vyacheslav 1.2.2005, 12:41
1) Он будет пытаться приводить все компоненты. Если это не удается, то Edit'у будет привоен NULL
2) Обрати внимание: у меня AnsiString clName = "TEdit";

Автор: azesmcar 1.2.2005, 13:49
Vyacheslav

clName == Components[i]->ClassName() работать не станет потому что типа ShortString а clName AnsiString..нету такого оператора сравнивания...

И зачем делать dynamic_cast??? Для dynamic_cast тебе нужно будет включить в проект RTTI (Run Time Type Information)..не очень весело...по моему так лучше...

Код

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 ShortString clName = "TEdit";
 for (int i=0; i < ComponentCount; i++)
 {
     if (clName == Components[i]->ClassName())
     {
         (static_cast<TEdit*>(Components[i]))->Text = "Some Text";
     }
 }
}

Добавлено @ 13:50
У AnsiString -а есть оператор сравнивания, но какой в этом смысл??? обяви сразу ShortString и покончь с делом...

Автор: Vyacheslav 1.2.2005, 15:26
Цитата(azesmcar @ 1.2.2005, 13:49)
Vyacheslav

clName == Components[i]->ClassName() работать не станет потому что типа ShortString а clName AnsiString..нету такого оператора сравнивания...

И зачем делать dynamic_cast??? Для dynamic_cast тебе нужно будет включить в проект RTTI (Run Time Type Information)..не очень весело...по моему так лучше...

Код

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 ShortString clName = "TEdit";
 for (int i=0; i < ComponentCount; i++)
 {
     if (clName == Components[i]->ClassName())
     {
         (static_cast<TEdit*>(Components[i]))->Text = "Some Text";
     }
 }
}

Добавлено @ 13:50
У AnsiString -а есть оператор сравнивания, но какой в этом смысл??? обяви сразу ShortString и покончь с делом...

1) Это работает по правилам С++ .
Если не веришь, проверь хотя бы в тупую код. А после этого может быть найдешь объясненние. Если не найдешь, я объясню
А вот ShortString оператора сравнения нет, поэтому твой код работать не будет

2) А ты пробовал собрать программу с использованием VCL без RTTI. Или ты думаешь, что ClassName обходится без RTTI?
Тогда внимательно прочитай help про TObject
Цитата

TObject encapsulates fundamental behavior common to VCL objects by introducing methods that

respond when object instances are created or destroyed.
return class-type and instance information on an object and runtime type information (RTTI) about its published properties.
support message handling.





Автор: azesmcar 1.2.2005, 17:06
Цитата
2) А ты пробовал собрать программу с использованием VCL без RTTI. Или ты думаешь, что ClassName обходится без RTTI?
пробовал и собирал!

Код

ShortString clName = "TEdit";


можно просто заменить на

Код

String clName = "TEdit";

У меня билдера нету, так на память помнил что вроде бы так...вот так точно работает...
Вот и все, а прочесывать всю иерархию для всех обьектов dynamic_cast -ом нахожу нецелеобразным...если обьяснишь почему вышеупомянутое правильно и обоснуешь я с тобой соглашусь...dynamic_cast довольно сильно замедляет программу, в данном случае он бессмысленен, если можно проверить тип перед кастингом...а насчет RTTI я уже сказал..Пробовал, собирал, работает...

Автор: Vyacheslav 1.2.2005, 17:59
Цитата

пробовал и собирал!

Ну-ну smile

Цитата

Код

ShortString clName = "TEdit";


можно просто заменить на

Код

String clName = "TEdit";


Ну-ну. String, говоришь?
Загляни- ка в symac.h. Это ни о чем не говорит? smile
Код

typedef  AnsiString           String;        


Цитата

У меня билдера нету, так на память помнил что вроде бы так...вот так точно работает...
Вот и все, а прочесывать всю иерархию для всех обьектов dynamic_cast -ом нахожу нецелеобразным...если обьяснишь почему вышеупомянутое правильно и обоснуешь я с тобой соглашусь...dynamic_cast довольно сильно замедляет программу, в данном случае он бессмысленен, если можно проверить тип перед кастингом...а насчет RTTI я уже сказал..Пробовал, собирал, работает...

Я не собираюсь выводить тебя из твоего сладкого заблужения о возможности исключить RTTI из VCL. Только вот прежде чем делать такие заявления, надо хотя бы иметь основания
А в реалиях имеем следущее
Вот этот код без использования VCL
Код

class A {
public:
   virtual func() {std::cout << "A"<< std::endl; }
};

class B : public A {
public:
   func() {std::cout << "B"<< std::endl; }

};
#pragma argsused
int main(int argc, char* argv[])
{
   B b;
   A* pa = &b;
   if (B* pb = dynamic_cast<B*>(pa))
   {
     std::cout << "RTII enabled" << std::endl;
     pb->func();
   }
   else
     std::cout << "RTII disbled" << std::endl;
   return 0;
}

При попытке отрубить RTTI не откомпилируется с выдачей ошибки и будет совершенно прав
Цитата

E2318 'A' is not a polymorphic class type Compiler error

Для этого достаточно в опциях отключить флажок Enable RTTI или в параметра ком. строки добавить -RT-
А вот этот код с VCL
Код

void __fastcall TForm1::Button1Click(TObject *Sender)
{
for (int i=0; i < ComponentCount; i++)
{
    if (TEdit* Edit = dynamic_cast<TEdit*>(Components[i]))
    {
        ShowMessage(Edit->Name);
    }
}
   
}

будет спокойно собираться и работать в любом случае: будешь ли ставить галочку или не будешь, будешь добавлять -RT- или нет. То есть если подключен VCL компилятору глубоко наплевать какие опции там стоят, ибо VCL без RTTI работать не способен и к VCLу по крайней мере его RTTI будет подключен обязательно
А насчет использовать, или не использовать. Напиши мне пожайлуста код, который у всех Controlo'v на форме поменяет цвет. Или хотя бы представь, что кроме TEdit, на форме есть еще TMyEdit, TEditEX, котрые порождены от TCustomEdit и у всех у них нужно очитстить Text

Автор: Adil' 1.2.2005, 18:12
Попробовать собрать VCL-ную прогу с отключенным RTTI можно, да вот только при этом RTTI у VCL не отключится smile
бо как все оно от TObject идет, и к нему и приходит, а он, родной, объявлен как DELPHICLASS, а значит и он сам, и наследники его автоматом RTTI поддерживают. Так что отключение RTTI в опциях проекта влияет только на "пользовательские" классы, которые без __rtti объявлены.

А кто больше замедляет программу - dynamic_cast или сравнение строк с вызовом виртуальных функций еще проверить надо, хотя я процентов на 90 уверен, что первое - быстрее.

Цитата(azesmcar @ 1.2.2005, 17:06)
можно просто заменить на

А где-то в хэдерах билдера есть такое typedef AnsiString String;

Добавлено @ 18:15
О, пока расписывал, Vyacheslav уже отповедь выдал smile

Автор: Vyacheslav 1.2.2005, 18:27
Цитата(Adil @ 1.2.2005, 18:12)
Так что отключение RTTI в опциях проекта влияет только на "пользовательские" классы, которые без __rtti объявлены.


Совершенно верно. И dynamic_cast для VCL классов будет работать в любом случае. А раз тащить его все равно приходится, то грех не воспользоваться

Автор: azesmcar 2.2.2005, 08:49
Цитата
Напиши мне пожайлуста код, который у всех Controlo'v на форме поменяет цвет. Или хотя бы представь, что кроме TEdit, на форме есть еще TMyEdit, TEditEX, котрые порождены от TCustomEdit и у всех у них нужно очитстить Text


Пожалуйста

Код

void __fastcall TForm1::Button1Click(TObject *Sender)
{
String clName = "TEdit";
for (int i=0; i < ComponentCount; i++)
{
    if (clName == Components[i]->ClassName())
    {
        (static_cast<TCustomEdit*>(Components[i]))->Text = "";
    }
}
}


Цитата
Ну-ну. String, говоришь?
Загляни- ка в symac.h. Это ни о чем не говорит?

Код 

typedef  AnsiString          String;     


Боже мой???!!! Неужели это то же самое??? Какой ужас???
А если так написать??? smile
#define String AnsiString

Надо же, ты гений!!! smile

А можно например вот так написать
Код

void __fastcall TForm1::Button1Click(TObject *Sender)
{
String clName = "TEdit";
for (int i=0; i < ComponentCount; i++)
{
    if (clName == Components[i]->ClassName())
    {
        const TCustomEdit* t = static_cast<TCustomEdit*>(Components[i]);
        TCustomEdit* p = const_cast<TCustomEdit*>(t);
        p->Text = "";
    }
}
}


Все равно в VCL это все уже есть, почему бы не попользоваться?? Грех! Ну разве что потому что медленее...чуточку..а dynamic_cast не очень то чуточку...я не спорю, я предлагаю вариант...

А насчет RTTI не знаю, никогда не интересовался конструкцией VCL, я в GUI не люблю работать...
Знаю только что пробовал, а вот отключил он или не отключил понятия не имею...

Автор: azesmcar 2.2.2005, 09:12
Цитата
А кто больше замедляет программу - dynamic_cast или сравнение строк с вызовом виртуальных функций еще проверить надо, хотя я процентов на 90 уверен, что первое - быстрее.


Ну проверить так проверить...smile Давай проверять, я думаю что второе быстрее будет

Автор: Vyacheslav 2.2.2005, 10:31
Ты дал опять нерабочий код
Цитата

Код

void __fastcall TForm1::Button1Click(TObject *Sender)
{
String clName = "TEdit";
for (int i=0; i < ComponentCount; i++)
{
    if (clName == Components[i]->ClassName())
    {
        (static_cast<TCustomEdit*>(Components[i]))->Text = "";
    }
}
}


ClassName() вернет для компонентов TMyEdit, TEditEX соответственно "TMyEdit" и "TEditEX" и они в условие
Код

 if (clName == Components[i]->ClassName())

никак не попадут Так что "гений" это , пожалй не я, а ты.

Цитата

Боже мой???!!! Неужели это то же самое??? Какой ужас???
А если так написать??? smile
#define String AnsiString
Надо же, ты гений!!! smile

Это к чему? Еще раз. В VCL String синоним AnsiString. Строку
typedef AnsiString String;
не я написал, а те кто разрабатывал VCL. Так что твое предложение заменить AnsiString на String кроме декоративности никакого смысла не несет, по скольку это по определению одно и тоже. Дошло, "гений"?
Цитата

А можно например вот так написать
Код

void __fastcall TForm1::Button1Click(TObject *Sender)
{
String clName = "TEdit";
for (int i=0; i < ComponentCount; i++)
{
    if (clName == Components[i]->ClassName())
    {
        const TCustomEdit* t = static_cast<TCustomEdit*>(Components[i]);
        TCustomEdit* p = const_cast<TCustomEdit*>(t);
        p->Text = "";
    }
}
}



Лучше вообще не писать. Заменить один неработающий вариант на другой не имеет смысла
Решение элементарно и лаконично, как и положено в С++
Код

void __fastcall TForm1::Button1Click(TObject *Sender)
{
for (int i= ComponentCount; i--;)
{
    if (TCustomEdit* Edit = dynamic_cast<TCustomEdit*>(Components[i]))
    {
        Edit->Text = "";
    }
}
}



Цитата

А насчет RTTI не знаю, никогда не интересовался конструкцией VCL, я в GUI не люблю работать...
Знаю только что пробовал, а вот отключил он или не отключил понятия не имею...


А нафига тогда в дискуссию влезать? И утверждать, что собирал VCL без его RTTI. Ради накручивания счетчика сообщений?


Кстати. А это то вообще зачем? Для увеличения скорости, о которой ты так печешься ? smile Или исключительно продемонстрировать свои знания const_cast?
Цитата

Код

const TCustomEdit* t = static_cast<TCustomEdit*>(Components[i]);
 TCustomEdit* p = const_cast<TCustomEdit*>(t);


Автор: azesmcar 2.2.2005, 11:44
Тебе весь код по кусочкам нужен??? Перечислить if для всех типов данных???
Хорошо
Код

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  String Edit = "TEdit";
  String EditEx = "TEditEx";
  for (int i=0; i < ComponentCount; i++)
  {
     if (Edit == Components[i]->ClassName() || EditEx == Components[i]->ClassName())
    {
        (static_cast<TCustomEdit*>(Components[i]))->Text = "";
    }
  }
}


Если я теперь не напишу еще один if для всех остальный тобою перечисленных типов ты не обидешся???;)

Хотя вот здесь скажу, dynamic_cast подойдет лучше, но вопрос задавался не для этого случая...

Дальше...
Цитата
А нафига тогда в дискуссию влезать? И утверждать, что собирал VCL без его RTTI. Ради накручивания счетчика сообщений?


А я не утверждал...ты спросил пробовал ли я собирать VCL проект без RTTI, я ответил что пробовал, и он собрался...

Цитата
Это к чему? Еще раз. В VCL String синоним AnsiString. Строку
typedef AnsiString String;
не я написал, а те кто разрабатывал VCL. Так что твое предложение заменить AnsiString на String кроме декоративности никакого смысла не несет, по скольку это по определению одно и тоже. Дошло, "гений"?


так, ну тут и отвечать нечего, по моему ты просто не понял что я написал...

ладно, скажу так
Я ЗНАЮ ЧТО String и AnsiString это одно и тоже
OБЬЯВЛЕНИЕ typedef в файле видел
и уже давно не использую в программах текст AnsiString всегда пишу String

Просто смотря на мой код ты подумал что у мое бурное воображение привело меня к тому что я решил запихнуть сюда STL и написал слово string с большой буквы по ошибке...ну да ладно...

Цитата
Кстати. А это то вообще зачем? Для увеличения скорости, о которой ты так печешься ?  Или исключительно продемонстрировать свои знания const_cast?


Нет, это для того чтобы показать что есть множество способов, но не все годятся для данного случая...

Если нужно проверить скорость то давай проверять...
Я так понимаю сравнение строк будет идти так

ABCD
ACEE
первые символы равны, continue
вторые символы не равны, return false
не очень то медленно...а dynamic_cast помнится вроде Страуструп советовал использовать только в ооочень деликатных ситуациях...

Если иерархия классов такая что другими методами кастинга на желаемый обьект выйти нельзя...
здесь иерархия примитивная...

Автор: Guest 2.2.2005, 14:33
Если Edit'ы заполняешь из базы, то самое простое:
- присваивай имена контролам в соответствии с именами полей в базе,
- создай класс, что-нибудь типа:

Код

typedef struct {
    TComponent     *component;
    AnsiString      value, name;
} TSimpleField;

typedef std::vector<TSimpleField> TFormFields;

class TControlsList {
private:
TFormFields::iterator     iter;

public:
TADODataSet *ds;
TFormFields    ValField;
...

void __fastcall CreateFields(TComponent *ct);
void __fastcall SetValueFields(void);
...
}

*.cpp

//в цикле вызвать для всех нужных компонентов формы
void __fastcall TControlsList::CreateFields(TComponent *ct)
{
unsigned at = ValField.size();
ValField.resize(at + 1);
ValField.at(at).component = ct;
ValField.at(at).value = "";
ValField.at(at).name = ct->Name;
}

void __fastcall TControlsList::SetValueFields(void)
{
TField *fld;
//где-то прочитал из базы

for ( ds->First(); !ds->Eof; ds->Next() )
{
    for ( iter = ValField.begin(); iter < ValField.end(); ++iter )
    {
       fld = ds->FindField( (*iter).name );  //ищешь поле по имени контрола
       if ( fld )
          if ( (*iter).component->ClassNameIs("TEdit") )
            {
              (*iter).value = fld->AsString;   //сохраняется начальное значение поля
              ((TEdit*)(*iter).component)->Text = fld->AsString;
            }
     }
}


для формы с большим количеством полей вполне нормально

Автор: azesmcar 2.2.2005, 14:36
Цитата
Если Edit'ы заполняешь из базы, то самое простое:
- присваивай имена контролам в соответствии с именами полей в базе,
- создай класс, что-нибудь типа:


А разве в этом есть смысл??? В билдере есть TDBEdit...

Автор: Guest 2.2.2005, 14:47
Есть смысл. Не всегда хочется пользоваться ими. (Только, давайте не будем дискутировать еще и на эту тему).

Автор: Hroft 2.2.2005, 15:19
azesmcar, я, конечно, извиняюсь, но ты, похоже, не понял почти ничего из постов
Vyacheslav'а, или просто не захотел понять. С начала перечитай весь топик, только без предосуждения, и успокойся, наконец.
Динамик каст использовать, может, и не рекомендуется, но у нас тут билдер, вполне конкретный компилятор. Решение Vyacheslav'а, на мой взгляд, красивое и полное.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)