Здравствуйте, Red Line, Вы писали:
RL>Нет не обиделся, просто не могу сказать, что мне это приятно...
Ну тогда извини.
RL>Да действительно те книги которые прочитал буквально вскользь затрагивали тему этой дискуссии. RL>Сейчас по почте закаказал книгу Страуструпа, буду грызть его
+1
RL>Весь смысл задаваемых мной вопросов сводится к пониманию следующего кода: RL>(из книги Дейла Роджерсона "Основы технологии COM").
Здравствуйте, adontz, Вы писали:
A>уууу! Так ты с Си на СОМ пересел? Смело
Это прямо в точку
Так и есть
Все вопросы связанные с мнжественным наследованием и виртальными функцими мне понятны
(качнул книгу "С++ бархатный путь" избранные главы).
Меня тормозят следующие моменты:
Здравствуйте, Red Line, Вы писали:
RL>Меня тормозят следующие моменты:
Рассмотрим интерфейс ISome и смарт-птр к нему, CComPtr<ISome> == CSomePtr;
RL>// Преобразование
RL>operator T*() { return m_pI; }
CSomePtr ptr;
void foo(ISome* p);
ISome* p = ptr;
foo(ptr);
RL>// Операции с указателем
RL>T& operator*() { assert(m_pI != NULL); return *m_pI; }
// эквивалентные способы вызвать метод: оператор-> и разыменовать...
((ISome*)ptr)->method();
(*((ISome*)ptr)).method();
(*ptr).method();
RL>T** operator&() { assert(m_pI == NULL); return &m_pI; }-------- > только этот понятен
// Кстати, довольно зловредный оператор. В ряде случаев мешает.
// Лучше уж ввести в CSomePtr метод reference(), возвращающий ссылку на переменную-член
RL>T* operator->() { assert(m_pI != NULL); return m_pI; }
// оператор доступа
((ISome*)ptr)->method();
ptr->method();
Оператор -> — занятная фича языка С++. В выражении obj->member выполняется следующее:
result_of_arrow_1 t1 = obj.operator->();
// если result_of_arrow_1 - не указатель, то
result_of_arrow_2 t2 = t1.operator->();
// если опять не указатель, то
result_of_arrow_3 t3 = t2.operator->();
...
// наконец, tN - это указатель
(*tN).member
RL>Покажите пожалуйста пример использования оператора (члена класса) объявленного так:
RL>
RL>Посмотрим, как выглядят операторы-члены.
RL>1) инфиксные двухместные (например, x+y). При этом левый аргумент (подразумеваемый) - это сам объект, а правый указывается в сигнатуре. Сравни:
RL>class C
RL>{
RL>public:
RL> // функциональная форма
RL> static C add(const C& lhs, const C& rhs);
RL> C operator + (const C& rhs) const { return add( *this, rhs ); }
RL>};
RL>
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Red Line, Вы писали:
RL>>Меня тормозят следующие моменты:
К>Рассмотрим интерфейс ISome и смарт-птр к нему, CComPtr<ISome> == CSomePtr;
К>
RL>>// Преобразование
RL>>operator T*() { return m_pI; }
К>CSomePtr ptr;
К>void foo(ISome* p);
К>ISome* p = ptr;
К>foo(ptr);
RL>>// Операции с указателем
RL>>T& operator*() { assert(m_pI != NULL); return *m_pI; }
К>// эквивалентные способы вызвать метод: оператор-> и разыменовать...
К>((ISome*)ptr)->method();
К>(*((ISome*)ptr)).method();
К>(*ptr).method();
RL>>T** operator&() { assert(m_pI == NULL); return &m_pI; }-------- > только этот понятен
К>// Кстати, довольно зловредный оператор. В ряде случаев мешает.
К>// Лучше уж ввести в CSomePtr метод reference(), возвращающий ссылку на переменную-член
RL>>T* operator->() { assert(m_pI != NULL); return m_pI; }
К>// оператор доступа
К>((ISome*)ptr)->method();
ptr->>method();
К>
К>Оператор -> — занятная фича языка С++. В выражении obj->member выполняется следующее: К>
К>result_of_arrow_1 t1 = obj.operator->();
К>// если result_of_arrow_1 - не указатель, то
К>result_of_arrow_2 t2 = t1.operator->();
К>// если опять не указатель, то
К>result_of_arrow_3 t3 = t2.operator->();
К>...
К>// наконец, tN - это указатель
К>(*tN).member
К>
// Операции с указателем RL>>T& operator*() { assert(m_pI != NULL); return *m_pI; }
Я ни как не вразумею, — как нужно читать синтаксис этого оператора.
Мне понятна запись:
С operator + (const C lPar, const C rPar);
Читаю (в соответствии с тем как Вы меня учили)
Внешний оператор;
двухместный инфиксный оператор (операнд слева — lPar, операнд справа — rPar);
Возможны две формы вызова
явная:
C x,y,z;
z = +(x,y);
и операторная:
С x,y,z;
z = x+y;
Верно?
Теперь оператор-член класса:
class С
{
C add(val1, val2);
C operator + (C rPar) const {return add(*this, rPar);}
};
Оператор-член класса; (подразумевается, что если левый аргумент не указан то это всегда *this)
двухместный (бинарный) и вроде бы инфиксный;
Явная форма вызова:
С obj;
C anyPar;
C res;
res = obj.operator + (anyPar); //равносильно obj+anyPar? Т.е текущий объект складывается с параметром, и что будет блин не догоняю ((
Операторная форма:
С obj;
C anyPar;
C res;
res = +anyPar;// правый операднд (параметр) указан явно, а левый есть объект. Какой??? anyPar-объект, а тот что в операторе где????
Мне бы хотелось в такой же форме разобрать и эти операторы:
RL>// Операции с указателем
RL>>>T& operator*() { assert(m_pI != NULL); return *m_pI; }
RL>Я ни как не вразумею, — как нужно читать синтаксис этого оператора.
Проще некуда:
struct Data
{
int x;
void foo();
};
// некоторые функции...void accept_data(Data d); // входной параметрvoid fill_data(Data& d); // выходной параметрstruct SmartPtr
{
Data* p;
Data& operator* () { return *p; }
};
main()
{
Data* p1;
SmartPtr p2;
. . . . .
// обычный указатель // явный доступ к члену умного // то же самое,
// -- обычному указателю // но завёрнутое в оператор разыменования
fill_data(*p1); fill_data(*(p2.p)); fill_data(*p2);
accept_data(*p1); accept_data(*(p2.p)); accept_data(*p2);
}
Видишь, что внешне работа с обычным указателем (левый столбец) выглядит так же, как с умным (правый).
Перекрытый оператор служит именно этой цели: одинаковости внешнего вида.
RL>Мне понятна запись: RL>
RL>С operator + (const C lPar, const C rPar);
RL>
RL>Читаю (в соответствии с тем как Вы меня учили) RL>Внешний оператор; RL>двухместный инфиксный оператор (операнд слева — lPar, операнд справа — rPar);
RL>Возможны две формы вызова RL>явная:
RL>C x,y,z;
RL>z = +(x,y);
RL>и операторная:
RL>С x,y,z;
RL>z = x+y;
RL>Верно?
Неверно!!!
Операторная запись:
z = x+y;
Функциональная запись (если оператор внешний):
z = operator+ (x,y);
Функциональная запись (если оператор — функция-член):
z = x.operator+ (y);
RL>Теперь оператор-член класса: RL>
RL> class С
RL>{
RL> C add(val1, val2);
RL> C operator + (C rPar) const {return add(*this, rPar);}
RL>};
RL>
RL>Оператор-член класса; (подразумевается, что если левый аргумент не указан то это всегда *this)
Он не "если не указан", а всегда не указан. У любого метода объекта есть неявный параметр — указатель на сам объект.
RL>двухместный (бинарный) и вроде бы инфиксный;
RL>Явная форма вызова:
// правый операднд (параметр) указан явно, а левый есть объект. Какой??? anyPar-объект, а тот что в операторе где????
Ещё раз. Операторная форма записи НИЧЕМ не отличается от операций над арифметическими типами.
Ключевое слово "operator" — это НЕ ОПЕРАТОРНАЯ ФОРМА ЗАПИСИ. А функциональная.
RL>Мне бы хотелось в такой же форме разобрать и эти операторы: RL>
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Red Line, Вы писали:
К>
RL>>// Операции с указателем
RL>>>>T& operator*() { assert(m_pI != NULL); return *m_pI; }
К>
RL>>Я ни как не вразумею, — как нужно читать синтаксис этого оператора.
К>Проще некуда: К>
К>struct Data
К>{
К> int x;
К> void foo();
К>};
К>// некоторые функции...
К>void accept_data(Data d); // входной параметр
К>void fill_data(Data& d); // выходной параметр
К>struct SmartPtr
К>{
К> Data* p;
К> Data& operator* () { return *p; }
К>};
К>main()
К>{
К> Data* p1;
К> SmartPtr p2;
К> . . . . .
К> // обычный указатель // явный доступ к члену умного // то же самое,
К> // -- обычному указателю // но завёрнутое в оператор разыменования
К> fill_data(*p1); fill_data(*(p2.p)); fill_data(*p2);
К> accept_data(*p1); accept_data(*(p2.p)); accept_data(*p2);
К>}
К>
К>Видишь, что внешне работа с обычным указателем (левый столбец) выглядит так же, как с умным (правый). К>Перекрытый оператор служит именно этой цели: одинаковости внешнего вида.
RL>>Мне понятна запись: RL>>
RL>>С operator + (const C lPar, const C rPar);
RL>>
RL>>Читаю (в соответствии с тем как Вы меня учили) RL>>Внешний оператор; RL>>двухместный инфиксный оператор (операнд слева — lPar, операнд справа — rPar);
RL>>Возможны две формы вызова RL>>явная: К>
RL>>C x,y,z;
RL>>z = +(x,y);
К>
RL>>и операторная: К>
RL>>С x,y,z;
RL>>z = x+y;
К>
RL>>Верно?
К>Неверно!!! К>Операторная запись: К>
z = x+y;
К>Функциональная запись (если оператор внешний): К>
z = operator+ (x,y);
К>Функциональная запись (если оператор — функция-член): К>
z = x.operator+ (y);
RL>>Теперь оператор-член класса: RL>>
RL>> class С
RL>>{
RL>> C add(val1, val2);
RL>> C operator + (C rPar) const {return add(*this, rPar);}
RL>>};
RL>>
RL>>Оператор-член класса; (подразумевается, что если левый аргумент не указан то это всегда *this)
К>Он не "если не указан", а всегда не указан. У любого метода объекта есть неявный параметр — указатель на сам объект.
RL>>двухместный (бинарный) и вроде бы инфиксный;
RL>>Явная форма вызова: К>
К>// правый операднд (параметр) указан явно, а левый есть объект. Какой??? anyPar-объект, а тот что в операторе где????
К>Ещё раз. Операторная форма записи НИЧЕМ не отличается от операций над арифметическими типами. К>Ключевое слово "operator" — это НЕ ОПЕРАТОРНАЯ ФОРМА ЗАПИСИ. А функциональная.
RL>>Мне бы хотелось в такой же форме разобрать и эти операторы: RL>>
// обычный указатель // явный доступ к члену умного // то же самое,
К> // -- обычному указателю // но завёрнутое в оператор разыменования
К> fill_data(*p1); fill_data(*(p2.p)); fill_data(*p2);
К> accept_data(*p1); accept_data(*(p2.p)); accept_data(*p2);
Кажется я понял !!!
fill_data(*p2);
accept_data(*p2);
*p2 — это и есть вызов операторной функции, т.е не звёздочка сама по себе, а именно "*p2" и есть вызов
аналогичный p2.operator*() для экземпляра p2... Этот вызов приведёт к тому, что отработает код:
return *p;
и функция accept_data получит аргумент верного тип (типа Data).
Здравствуйте, Red Line, Вы писали:
RL>Кажется я понял !!!
RL>
RL> fill_data(*p2);
RL> accept_data(*p2);
RL>
RL>*p2 — это и есть вызов операторной функции, т.е не звёздочка сама по себе, а именно "*p2" и есть вызов RL>аналогичный p2.operator*() для экземпляра p2... Этот вызов приведёт к тому, что отработает код:
RL>
RL> return *p;
RL>
RL> и функция accept_data получит аргумент верного тип (типа Data).
RL>Ну так или нет?
> ПК>Такой подход — прямая дорога к замусориванию языка. Далеко не все "фичи" хорошо уживаются вместе (яркий пример — спецификация исключений и шаблоны). > > А как они не уживаются? Или ты про то, что сделав catch( complex<int> ) я не поймаю catch( complex<float> ) ?
Добавлять спецификацию исключений к шаблонам функций — зачастую совершенно бесперспективное занятие. Также наличие шаблонов закрывает простые пути проверки спецификации исключений на этапе компиляции. Но в данном случае полезность по-крайней мере одной из этих двух "фич" очевидна
> ПК>Что ты подразумеваешь под "универсальным type_traits"? Какое конкретное ты видишь применение "указателям на конструкторы" и "указателям на деструкторы"? > > Например, (моя идея-фикс) определение PODности типов.
Дык, а причем здесь адреса конструкторов? Это более общая проблема: кроме определения является ли данный тип POD-типом, в желаемых type_traits есть еще очень много вещей, где нужна поддержка компилятора. Соответственно, вводить бесполезную саму по себе фичу только для того, чтобы решить только одну проблему в type_traits, при том, что все равно будут нужны какие-то решения для остальных моментов, — я просто не вижу смысла.
> ПК>Перечисли характерные особенности "указателей на конструкторы" и "указателей на деструкторы". В частности: > ПК> Каковы правила приведения между "указателями на конструкторы" разных типов, "указателями на деструкторы"? > > Никаких правил. Только к void * и обратно.
оценки сообщению прошу считать признанием полезности фичи
В общем, я понял: сами по себе указатели на конструкторы тебе не нужны, тебе нужна возможность определения является ли некоторый тип POD-типом Против последнего особых возражений нет.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен