Наследование. Каким образом вызывается operator=() через оператор разрешения контекста?
Читал про наследование с динамическим выделением памяти одновременно: и в производном, и в базовом классах. И есть такая штука в примере с определением operator=() производного класса: BaseClass::operator=(derived_reference);
Как так? Ведь operator=() - не статический метод, а значит его вызывать можно только через объект. Ниже написано, что это почти равнозначно следующему: *this = derived_reference;
Только в этом случае будет рекурсивный вызов, т.к. будет преобразовано в: DerivedClass::operator=(derived_reference);
Это такая особенность, созданная для наследования, или как? Ведь по правилам, на сколько я знаю, так делать никак нельзя.
Это не вызов статического метода, а явное указание того, какую реализацию использовать (не полиморфно, будет вызванная именн офункция для указанного класса). Рекомендую почитать про множественное наследование, там может станет чуть понятнее. Например, если класс С унаследован от А и B, и в них обоих есть переменная var, то нужно будет писать A::var и B::var, чтобы пояснить, какая из них исползьуется.
Я как раз читаю Си++ Прата. Я не понимаю, почему это работает. Нельзя ведь вызывать метод просто через область видимости (разрешение контекста), если он не статический. Если нужно вызывать собственный метод, то есть указатель this, и следует вызывать метод через него. Проблема в том, что тут нужно вызвать нестатический метод базового класса, а объект указать невозможно, т.к. указателя наподобие this на базовый класс не существует. Вот я и не пойму, что это за диковина такая.
С другой стороны, там написано, что это необходимо делать только если и базовый, и производный класс используют динамическое выделение памяти. Но я догадываюсь, что такое нужно делать просто при явном определении operator=(). Иначе логика теряется. Явно определять operator=() можно и без использования выделения памяти на куче.
Egorithm, Это никак не связано с динамическим выделением памяти.
class A
{
A(int);
};
class B
{
B( int b )
: A(b)
{}
};
Например, вот. В конструкторе указывается конструктор базового класса. Примерно то же самое делается для методов. Условно выражаясь, происходит вызов метода через "this-родительского-класса" с отключенным полиморфизмом, т.е. вызовется именно метод базового класса, а не перегруженный метод.
Есть и ещё более запутанные приёмы. Например:
class A
{
virtual void func();
};
class B : public A
{
virtual void func();
};
class C : public B
{
virtual void func()
{
... // какой-т код
A::func();
... /// ещё какой-то код
}
};
В методе func класса С вызывается реализация метода func из класса А, при этом реализация из класса B не вызывается.
Egorithm, > С другой стороны, там написано, что это необходимо делать только если и базовый, и производный класс используют динамическое выделение памяти. Но я догадываюсь, что такое нужно делать просто при явном определении operator=().
Подозреваю, что там речь идёт о необходимости самостоятельно писать operator=(), если ВНУТРИ объекта происходит динамическое выделение памяти, потом учто компилятор сам не может знать, как сравнивать объекты с указателями. Т.е. речь не о способе создания самого объекта, а о способе создания его членов.
В любом случае, если создаёшь конструктор копирования - создаёшь оператор присваивания. Если создаёшь оператор присваивания - создавай и конструктор копирования.
maaGames, мне на Stack Overflow подсказали, что можно и так делать this->BaseClass::operator=(derref);
Так даже правильнее и очевиднее, что происходит
Просто я думал, что неявное обращение к членам (без this) работает только с данными-членами, но не с функциями-членами. Но, как оказалось, я не правильно думал. Хотя, едва ли я стану писать иначе, потому что по мне это плохой стиль. Это неочевидно. Выглядит как статический метод или просто функция.
Egorithm, Внутри методов класса this-> всегда есть неявно, но можно указывать и явно, для более лучшего документирования кода. Или если имя аргумента метода совпадает с именем члена класса (но это уже проблемы именования).
Суть такого написания в том, чтобы не копировать реализацию из базового класса в дочерний. Так что может визуально выглядит не очень красиво и понятно, но это лучше, чем дублирование кода. Это относится не только к оператору присваивние, а к абсолютно всем виртуальным функцям. По возможности нужно избегать дублирование кода (упрощает отладку и дальнейшую поддержку кода), а вот такой "странный" вызов сильно упрощает эту задачу.