C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
ООП, Использование ключевого слова operator
Eugene
Отправлено: 03.06.2003, 09:28


Не зарегистрирован







Я создал класс, у которого определи оператор = и преобразование обекта этого класса к типу AnsiString
class aStringObject
{
private:
String myString;
public:
__fastcall aStringObject(void){};

operator String(void){return myString;};
aStringObject & operator= (String theString){myString = theString; return *this;};
}


Пример 1:
aStringObject a;
AnsiString b="Test string";
//Как написанно в книгах неявный вызов оператора =
a=b;
//эквивалентен явному вызову: a.operator=(cool.gif; те myString стало ="Test string"

Этот оператор = и operator String работают, если объект aStringObject создается статически как в примере 1.

Пример2(когда объект создается динамически):
aStringObject * a=new aStringObject;
AnsiString b="Test string";
//То неявный вызов оператора =
a=b;
//эквивалентен явному вызову: a.operator=(cool.gif; что приводит к ошибке, так как надо a->operator=(cool.gif;

Как сделать, чтобы работал пример2, то есть член-данное myString объекта a стало равно b, то есть "Test string".




Георгий
Отправлено: 03.06.2003, 11:14


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



aStringObject* — это не тоже самое, что aStringObject — это разные обьекты.
придётся разъименовывать aStringObject* и получать aStringObject
CODE

*a=b;


Отредактировано Георгий — 3 Jun 2003, 11:17
Eugene
Отправлено: 04.06.2003, 07:20


Не зарегистрирован







QUOTE (Георгий @ 3 Jun 2003, 11:14)
aStringObject* — это не тоже самое, что aStringObject — это разные обьекты.
придётся разъименовывать aStringObject* и получать aStringObject
CODE

*a=b;

А нельзя ли определить как-нибудь оператор = для указателя на объект, чтобы оставить код в виде
CODE

a=b;
Alexander
Отправлено: 04.06.2003, 12:57


Не зарегистрирован







Напиши, если так уж хочется:
[CODE]
AStringObject &a, *pa = new AStringObject;
a = *pa;

a = b;

[CODE]
Георгий
Отправлено: 04.06.2003, 14:06


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



адрес обьекта — это встроенный тип данных, который может использоватья как LValue только когда RValue может быть интерпретировано как адрес обьекта того же типа. Перегрузить оператор присваивания этого типа данных не представляется возможнам т.к. его описание как таковое отсутствует.

но если нужно динамическое создание экземпляра обьекта, то можно воспользоваться ссылками:
CODE

class aStringObject
     {
protected:
         AnsiString myString;
public:
       aStringObject(void)
                          {
                          Form1->Memo1->Lines->Add("aStringObject");
                          };
       ~aStringObject(void)
                           {
                           Form1->Memo1->Lines->Add("~aStringObject");
                           };
       aStringObject& operator=(AnsiString& newString)
                             {
                             this->myString=newString;
                             return *this;
                             };
       operator AnsiString(void)
                {
                return this->myString;
                };
     };

void __fastcall TForm1::Button1Click(TObject *Sender)
{
aStringObject asoStr;
AnsiString str2="asd";
str2=asoStr=str2;
//динамическое создание обьекта
aStringObject &asoDynamicStr=*new aStringObject;
str2=asoDynamicStr=str2;
//освобождение памяти от него
delete &asoDynamicStr;
}

Eugene
Отправлено: 05.06.2003, 10:44


Не зарегистрирован







А как сделать, чтобы объект типа aStringObject (создаваемый динамически) был член-данным какого-то класса при обеспечении вызова операторов = и AnsiStinrg.

Если класс описать так:
CODE

class MyClass
{
  public:
     aStringObject * DataString;

     __fastcall MyClass()
{
 DataString = new aStringObject;
};
     __fastcall ~MyClass(void){delete DataString;};
};

то использовать операторы можно так
CODE

   MyClass * ClassObject = new MyClass();
   String Str="Test string";
   ClassObject->DataString->operator=(Str);
   this->Edit1->Text=ClassObject->DataString->operator String();
   delete ClassObject;

или

   MyClass * ClassObject = new MyClass();
   String Str="Test string";
  *ClassObject->DataString=Str;
   this->Edit1->Text=*ClassObject->DataString;
   delete ClassObject;

Оба варианта использования операторов по-моему корявые

При попытке описать класс MyClass как
CODE

class MyClass
{
  public:
     aStringObject & DataString;

     __fastcall MyClass(void)
{
 DataString = * new aStringObject;
};
     __fastcall ~MyClass(void){delete &DataString;};
};

CBuilder ругается на первую скобку "{" конструктора MyClass():
[С++ Error] Reference member 'DataString' is not initialized.
В хелпере на эту ошибку написанно:
References must always be initialized, in the constructor for the class.

Вопрос: как сделать, чтобы работал код
CODE

   MyClass * ClassObject = new MyClass();
   String Str="Test string";
  ClassObject->DataString=Str;
   this->Edit1->Text=ClassObject->DataString;
   delete ClassObject;


Георгий
Отправлено: 05.06.2003, 14:30


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



смутное подозрение, что Вы бьётесь головой об стену...
Давайте уточним ряд вещей:
1. Язык C++ позволяет создавать свои типы данных и использовать их также, как и встроенные
2. Создание обьекта — это не самоцель — обьекты создают, чтобы перейти на другой уровень абстракций, с целью уменьшения сложности задачи (разделяй и властвуй).

хорошо предположим, что Вам понадобилось, ради каких-то неведомых целей, сделать обьект у которого есть поле, содержащее другой обьект — вполне нормальная ситуация. Можно сделать:
1. поле — объект
2. поле — адрес объекта (указатель или ссылка)
функционально эти варианты эквивалентны, но есть небольшие различия:
если нескользо объектов должны совместно владеть одним объектом, то придётся использовать адрес объекта в качестве поля.
если в поле должен храниться неизвестный потомок некоторого обьекта, то тут опять придётся использовать поле адрес.
во всех остальных случаях нет смысла использовать поле адрес.

по поводу ссылок — это замена указателям в некоторых случаях, но не более. Ссылки имеет смысл использовать при передаче в функции больших объектов и всё — в остальных случаях использование указателей позволяет более ясно представлять механизмы работы программы.

в Вашем случае самым разумным будет такая структура объекта:
CODE

class MyClass
{
public:
aStringObject DataString;
__fastcall MyClass();
__fastcall ~MyClass(void);
};


причём Ваш кусок кода будет работать
CODE

MyClass * ClassObject = new MyClass();
String Str="Test string";
ClassObject->DataString=Str;
this->Edit1->Text=ClassObject->DataString;
delete ClassObject;


Кстати — я не очень понимаю, зачем вы указали, что конструктор и деструктор используют соглашение __fastcall — в этом случае часть аргументов передаётся через регистры общего назначения и это уменьшает время вызова этих функций, но конструктор и деструктор вызываются относительно редко — и этот __fastcall не даст никакого увеличения скорости работы.

забыл сказать как поле ссылку обьекта проинициализировать:
CODE

class MyClass
{
public:
      aStringObject & DataString;
      MyClass(void);
      ~MyClass(void);
};

MyClass::MyClass(void):DataString(*new aStringObject)
{
};

MyClass::~MyClass(void)
          {
          delete &DataString;
          };


Отредактировано Георгий — 6 Jun 2003, 01:46
Eugene
Отправлено: 09.06.2003, 08:49


Не зарегистрирован







Цель моих вопросов — создать свойство вместе с редактором этого свойства.
Для использования редактора надо, чтобы это свойство было VCL классом (получается что-то вроде aStringObject, только унаследованное от TPersistent).
А объекты VCL классов можно создать только динамически.

С другой стороны я хочу обеспечить простой доступ к этому свойству для программиста как будто это свойство типа String(или др.) без каких-либо внутренних член-данных у этого свойства.

Вопрос: можно ли создать у компоненты свойство — ссылку (на тип aStringObject)?
Георгий
Отправлено: 13.06.2003, 18:14


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



можно сделать свойство, которое работает с ссылкой:

CODE

class aStringObject
    {
protected:
        AnsiString myString;
public:
      aStringObject (const AnsiString& s)
                    {
                    this->myString=s;
                    };
      aStringObject(void)
                         {
                         };
      ~aStringObject(void)
                          {
                          };
      aStringObject& operator=(AnsiString& newString)
                            {
                            this->myString=newString;
                            return *this;
                            };
      operator AnsiString(void)
               {
               return this->myString;
               };
    };


class MyClass
{
public:
     MyClass(void);
     ~MyClass(void);
       __property aStringObject MyString  = { read=GetMyString, write=SetMyString };
private:
       aStringObject & DataString;
       void __fastcall SetMyString(aStringObject& value);
       aStringObject& __fastcall GetMyString();
};

MyClass::MyClass(void):DataString(*new aStringObject)
{
};

MyClass::~MyClass(void)
         {
         delete &DataString;
         };


void __fastcall MyClass::SetMyString(aStringObject& value)
{
this->MyString=value;
}
aStringObject& __fastcall MyClass::GetMyString()
{
return this->MyString;
}


и работает такой код:
CODE

MyClass a;
AnsiString str="test";
a.MyString=str;
str=a.MyString;


это то, чего ты добивался?

кстати — в этом случае не обязательно использовать поле-ссылку — для поля указателя можно в методах Get и Set написать так:
CODE

void __fastcall MyClass::SetMyString(aStringObject& value)
{
(*this->MyString)=value;
}
aStringObject& __fastcall MyClass::GetMyString()
{
return *this->MyString;
}

Eugene
Отправлено: 14.06.2003, 11:42


Не зарегистрирован







Как я сказал выше, на самом деле класс aStringObject является унаследованным от TPersistent, то есть VCL классом.
Это приводит к тому, что при определении свойства этого типа как
CODE

class MyClass
{
public:
    MyClass(void);
    ~MyClass(void);
      __property aStringObject MyString  = { read=GetMyString, write=SetMyString };
private:
      aStringObject & DataString;
      void __fastcall SetMyString(aStringObject& value);
      aStringObject& __fastcall GetMyString();
};

приводит к ошибке в строке объявления свойства: Объект класса VCL должен создаваться используя оператор new. (VCL style classes must be constructed using operator new).

Объявление свойства как указателя на aStringObject мне не подходит.
При объявлении свойства как ссылки приводит к ошибке :
Parameter mismatch in write access specifier of property MyString.
Хотя я описал все возможные методы записи:
CODE

void __fastcall SetMyString(aStringObject & Value){this->DataString=Value;}
void __fastcall SetMyString(aStringObject Value){this->DataString=Value;}
void __fastcall SetMyString(aStringObject * Value){this->DataString=*Value;}

Использование прямого доступа к полю-ссылки DataString для записи
CODE

__property aStringObject MyString  = { read=GetMyString, write=DataString };

не приводит к ошибке на этапе компиляции и сборки.
Однако записать в свойство MyString не получается (в отличие от чтения):
CODE

       TMyClass * ClassObject=new TMyClass();
       ClassObject->MyString=String("Data");
       Edit1->Text=ClassObject->MyString;
       delete ClassObject;


Вопрос: можно ли создать у MyClass работающую свойство — ссылку (на тип aStringObject) и как? Или придется использовать свойство-указатель MyString для вызова редактора этого свойства из Object Inspector`а, а для "простого" доступа сделать член-данное aStringObject & DataString как public?

Вопрос о ссылках.
CODE

int Value1=1, Value2=2;
int & Value3=Value1;

Можно ли после такого объявление и определения ссылки Value3 сделать так, чтобы Value3 ссылался на Value2.
Георгий
Отправлено: 14.06.2003, 16:09


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



все VCL-обьекты — сделаны со свойствами-указателями, возможно именно из-за невозможности сделать иначе. Советую оставить свойство указатель, чтоб пользователи работали только с ним т.к. если они взялись за C++, то и с указателями должны свободно работать.

Можно ли после такого объявление и определения ссылки Value3 сделать так, чтобы Value3 ссылался на Value2.

Можно, но именно для этой последовательности обьявления переменных:
CODE

int Value1=1, Value2=2;
int & Value3=Value1;
*((void**)(&Value2)-1)=&Value2;//получаем настоящий адрес переменной ссылочного типа и записываем в него адрес Value2, после чего Value3 ссылается на Value2

код надо будет корректировать в зависимости от платформы (направление роста стека) размещения переменных в стеке и особенностей этого размещения. Но при использовании одной платформы (i386) и одного компилятора (bc) всё будет в порядке.

Вернуться в Вопросы программирования в C++Builder