LINUX.ORG.RU

C++ непонимание наследования классов

 


0

4

Здравствуйте, коллеги!

class Parent{
private:
  int i;
public:
  Parent& operator = (Parent &p){
    this->i = p.i;
  }
};

class Child: public Parent{

};

Мне нужно сделать конструкцию типа:

Parent p;
Child c;
c = p;

Т.е. что бы, по большому счету сработал перегруженный оператор «=» из родительского класса. Произошла инициализация "i"б находящийся в protected базового класса. Но я не понимаю как это сделать.



Последнее исправление: HighMan (всего исправлений: 2)
Ответ на: комментарий от AntonI

оператор присваивания по умолчанию так и сработает, не надо усложнять простые вещи.

Вышеприведенный пример максимально упрощен.

На деле, в родительском классе будут указатели на выделенные данные, а эти данные инициализированы соответствующим образом. Т.к. объекты классов объявлены статически, то после их дестроя будет лютый треш в дочерних классах.

HighMan
() автор топика
Ответ на: комментарий от AntonI

Оператор = сработает только для класса наследника, не для Parent.

Сам по себе оператор = не наследуется, но для класса автоматически генерируется новый. Но он генерируется только для Child.

ТС, тебе нужно просто определить в наследнике свой оператор=, в котором вызвать Parent::operator=(other). Ну и определить другие операторы присваивания, если нужны.

Ivan_qrt ★★★★★
()

Ты думаешь, что

Parent& operator = (Parent &p);

это метод класса, а на самом деле это внешняя функция

ReturnType operator Op(const ClassType& left_operand, Type right_operand);

которая объявлена внутри класса для удобства. Поэтому в механизме наследования она не участвует.

soomrack ★★★★★
()
Ответ на: комментарий от soomrack

которая объявлена внутри класса для удобства. Поэтому в механизме наследования она не участвует.

Хм… А разве функция вне класса может оперировать с данными из private секции?

HighMan
() автор топика
Ответ на: комментарий от Ivan_qrt
class Parent{
private:
  int i;
public:
  Parent& operator = (Parent &p){
    this->i = p.i;
  }
  Parent foo(){
    return *this;
  }
};

class Child: public Parent{
  Child & Parent::operator = (Parent &p)
};

Parent p;
Child c = p.foo() // Ошибка
c = p; //Так работает

Какой-то лютый бред с этим наследованием и статическими объектами классов…..

HighMan
() автор топика
Последнее исправление: HighMan (всего исправлений: 1)
Ответ на: комментарий от HighMan

Child& operator = (Parent &p) {
    this = Parent::operator=(p);
}

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

soomrack ★★★★★
()
Ответ на: комментарий от soomrack

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

На самом деле я сделал простенький класс-обертку Socket над socket.

Дальше я создаю еще один класс Talk, наследованный от Socket;

Socket s;
//.....
// Socket Accept(void)
Talk t = s.Accept();
HighMan
() автор топика
Ответ на: комментарий от HighMan
  • Я малость наврал в самом начале.

Функция перегрузки оператора в классе это метод класса и она НАСЛЕДУЕТСЯ, как и все обычные методы. Но ты его не вызываешь, ты вызываешь метод с другим типом данных (Child).

soomrack ★★★★★
()
Последнее исправление: soomrack (всего исправлений: 1)
Ответ на: комментарий от soomrack
(Parent) this = Parent::operator=(p);

Не нравится компилятору.

Child & Child::operator = (Parent &s){
    this->Parent::operator=(s);
    return *this;
}

вот так работает, но не дает

Child c = p.foo();
// но прекрасно работает
c = p;
HighMan
() автор топика
Ответ на: комментарий от AntonI
    (Parent&) *this = p;
    return *this;
}```

Ага. Так работает. 

Но не работает:

Parent Parent::foo(void){ return *this }

Parent p; Child c = p.foo() //ошибка: conversion from «Parent» to non-scalar type «Child» requested 101 | Child c = p.foo();

HighMan
() автор топика
Последнее исправление: HighMan (всего исправлений: 2)
Ответ на: комментарий от HighMan

#include <iostream>

class Parent {
public:
    int id;
public:
    Parent() { id = 1; };
    Parent(int number) { id = number; };
    Parent& operator=(Parent &source) { id = source.id; return *this; };
};


class Child : public Parent {
public:
    Child() {};
    Child& operator=(Parent &source) { Parent::operator=(source); return *this; };
};



int main() {
    Parent P(10);
    std::cout << "Parent.id = " << P.id << std::endl;

    Child C;
    std::cout << "Child.id = " << C.id << std::endl;

    C = P;
    std::cout << "Child.id = " << C.id << std::endl;
}

const, virtual и пр. в соотв. с задачей…

soomrack ★★★★★
()
Последнее исправление: soomrack (всего исправлений: 1)
Ответ на: комментарий от AntonI

И не должно. Вообще Вы уверены что хотите по значению возвращать там результат, а не по ссылке?

Parent& Parent::foo(){ return *this}

Так, как я понял, нельзя.

Можно только:

Parent Parent::foo(){ return *this}
HighMan
() автор топика
Ответ на: комментарий от Bad_ptr

Ага, с С++17 так уже можно писать и так правильней.

Но не могу не отметить еще раз, что задача ТС несколько далека от хорошего кода, присваивание подразумевает одинаковые типы данных с обеих сторон, если это не так, то выполняется неявное привидение типов, что сильно замутняет код, а для классов присваивание ребенку значение родителя это хрен пойми что.

soomrack ★★★★★
()
Ответ на: комментарий от Bad_ptr

А еще можно написать так, это понятней с т.з. того, что делает код, если нужно только скопировать данные из родителя (но все равно, это очень плохой стиль):

#include <iostream>


class Parent {
public:
    int id;
public:
    Parent() { id = 1; };
    Parent(int number) { id = number; };
    Parent& operator=(Parent &source) { id = source.id; return *this; };
};


class Child : public Parent {
public:
    Child() {};
//    Child& operator=(Parent &source) { Parent::operator=(source); return *this; };
};



int main() {
    Parent P(10);
    std::cout << "Parent.id = " << P.id << std::endl;

    Child C;
    std::cout << "Child.id = " << C.id << std::endl;

    *((Parent *)&C) = P;
    std::cout << "Child.id = " << C.id << std::endl;
}
soomrack ★★★★★
()
Последнее исправление: soomrack (всего исправлений: 1)
Ответ на: комментарий от HighMan

Parent p; Child c = p.foo() //ошибка: conversion from «Parent» to non-scalar type «Child»

Потому что тут «инициализация», «Child c» — это вызов конструктора, а «=» — это сразу передача аргумента в конструктор.

class Child : public Parent {
public:
    Child(Parent &p) : Parent(p) {};
    using Parent::operator=;
};

Bad_ptr ★★★★★
()
Ответ на: комментарий от HighMan

Здесь проблема не в наследовании.

Из p.foo() возвращается временный объект (rvalue). Этот временный объект может быть захвачен либо через const Type&, либо через Type&&. У тебя по всей видимости просто Type&, к нему временный объект не забиндить.

Соответственно тебе надо либо сделать оператор Parent& operator=(const Parent& other), либо добавить оператор move assignment Parent& operator=(Parent&& other).

Ну и в любом случае модифицировать при присваивании объект, который при этом захватывается не через rvalue-ссылку (&&) нужно только если ты понимаешь, что и зачем ты делаешь. По умолчанию у тебя всегда должен быть оператор присваивания (и соответствующий конструктор, если он есть) принимающий const ref.

С const Parent& оператором всё работает: https://godbolt.org/z/4cdjz3svz

Ivan_qrt ★★★★★
()
Ответ на: комментарий от HighMan

На самом деле я сделал простенький класс-обертку Socket над socket.

Одному мне кажется что оно должно быть «non-copyable»? Вы, походу, дичь затеяли. А про всякие move-semantics and rvalue-refs Вам пока, кмк, задумываться рановато.

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 1)
Ответ на: комментарий от bugfixer

Это классический вредный совет.

Возвращать ссылку на объект можно только если время жизни этого объекта не превышает времени жизни ссылки. За этим нужно следить и это понимать, а у ТСа сейчас явно есть гораздо более важные проблемы и добавлять себе новые с провисшими ссылками ему не нужно. Тем более, что они диагностируются только в рантайме и не всегда.

Так что ТС, возвращай копию без всяких ссылок и не заморачивайся лишним. Когда разберёшься сам поймёшь, где надо ссылки возвращать, а где копии.

Ivan_qrt ★★★★★
()
Ответ на: комментарий от Ivan_qrt

Возвращать ссылку на объект можно только если время жизни этого объекта не превышает времени жизни ссылки.

Причём здесь это? Ну давайте по значению всё таскать. Ну, и в качестве развлечения - посмотрите на asm…

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

При том, что из foo() возвращается либо ссылка на объект, либо его копия.

И с копией можно делать всё, что угодно. А ссылка работает только пока жив оригинальный объект.

Всё таскать по значению не надо и даже не удобно. Но вот вещи, которые могут быть переданы неизвестно куда вполне имеет смысл возвращать как копию, по-крайней мере до тех пор, пока нет полного понимания о временах жизни и нет уверенности, что объект переживёт все ссылки.

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

Ivan_qrt ★★★★★
()

Мне нужно сделать конструкцию типа:

Parent p;
Child c;
c = p;

это семантически неверная конструкция. это все равно что присвоить переменной типа Кошки(наследник класса Животное) актуальное значение Мышки(например).

Это нарушение типизации, зачем это вам НУЖНО, непонятно совершенно. Это противоречит здравой логике. Отцы не присваиваются детям, поскольку дети есть более специальный класс(содержит доп. поля и методы).

Если вам надо построить класс потомок по предку, то это делается конструктором. ну или какой-то функцией с хорошо читаемым именем, поскольку сама операция довольно специфична.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от Ivan_qrt

При том, что из foo() возвращается либо ссылка на объект, либо его копия.

В примере возвращают самого себя. Но то же самое было бы справедливо для любого поля класса. Я не знаю случая когда accessor по значению дешевле чем inline by-ref, и понятно почему.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

В примере возвращают самого себя

Какая разница? Даже если возвращать себя в любом случае вполне возможна ситуация, когда объект уже уничтожен, а ссылка на него осталась. В том, что я сказал это буквально ничего не меняет.

Я не знаю случая когда accessor по значению дешевле чем inline by-ref, и понятно почему.

Оно не дешевле (если не учитывать прокси объекты типа string_view, span и т.п.). Но проблема не в производительности, а в корректности. У тебя может быть супер быстрый код, но если он падает или портит память, то не важно насколько быстро он это делает.

Ivan_qrt ★★★★★
()
Ответ на: комментарий от Ivan_qrt

Возвращать ссылку на объект можно только если время жизни этого объекта не превышает времени жизни ссылки.

И кто ж в приведённых примерах такой злодей што *this убил?!

AntonI ★★★★★
()
Ответ на: комментарий от Ivan_qrt

вполне возможна ситуация

когда выданный стеклянный члены разбивается и осколки режут руки. Бывает, да. Референнсы возвращать боятся - на плюсах не писать.

AntonI ★★★★★
()
Ответ на: комментарий от AntonI

Был приведён только один пример использования foo(). Если это вообще единственное использование foo(), то она в принципе не нужна, поэтому предполагать такое как минимум странно.

Ну и я оспаривал не то, что можно возвращать ссылку, а то что нужно возвращать ссылку. Ссылку допустимо возвращать только в том случае, если программист в этом уверен. Это явно не случай ТСа, поэтому в его случае гораздо лучше возвращать копию.

Ivan_qrt ★★★★★
()
Ответ на: комментарий от Ivan_qrt

Какая разница?

Действительно:

auto foo = bar();

Жду контр-примеров когда это не будет работать с bar() возвращающим ссылку.

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 1)