Че-та где-та я подтупливаю с перегрузкой оператора

5N62V
Offline
Зарегистрирован: 25.02.2016

Ну собственно есть родительский класс, и дочерний. Пытаюсь перегрузить оператор =  так, чтобы члены дочернего класса инициировать родительским. 

class Parent{
  protected: 
  float a;
  float b;
  float c;
  public:

};

class Sub: public Parent{
  protected:
  float d;
  public:

  Sub& operator=(Parent& src){
a = src.a;
b = src.b;
c = src.c;
d = 0;    
    return *this;
  }
};




void setup() {
  // put your setup code here, to run once:
}

void loop() {
  Parent aaa;
  Sub bbb;
  bbb = aaa;

}

А в ответ :

sketch_jun07a:3: error: 'float Parent::a' is protected

   float a;

         ^

sketch_jun07a:16: error: within this context

 a = src.a;

         ^

sketch_jun07a:4: error: 'float Parent::b' is protected

   float b;

         ^

sketch_jun07a:17: error: within this context

 b = src.b;

         ^

sketch_jun07a:5: error: 'float Parent::c' is protected

   float c;

         ^

sketch_jun07a:18: error: within this context

 c = src.c;

         ^

exit status 1
'float Parent::a' is protected

что за фигня? Где я накосячил, подскажите плз. Я могу , конечно , метод инициации написать, но истина как бэ дороже! :)

ПС. Предвосхищая события: читать сообщение умею, но смысл написанного ускользает! :) И что, что протектед? Класс Sub ведь дочерний, вроде. 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Заменить надо public Parent -> private Public, вроде.

Я особо не вникал, но как понимаю - прогонять protected через public бессмысленно, так как идёт понижение "уровня защиты".

rkit
Offline
Зарегистрирован: 23.11.2016

Напиши friend class Sub; в базовом.

Сделано это потому, что аргумент типа Base может быть другой ветвью наследования. Например, если ты создал class Sub2: private Base, то передав его в функцию можно было бы читать его методы, хотя они объявлены как private в Sub2.

 

5N62V
Offline
Зарегистрирован: 25.02.2016

rkit пишет:

Напиши friend class Sub; в базовом.

Да, это работает, спасибо. Но как бы возникло у меня сомнение: а не иной ли это получился тип отношений между классами?

rkit
Offline
Зарегистрирован: 23.11.2016

Конечно иной. Потому и работает.

5N62V
Offline
Зарегистрирован: 25.02.2016

rkit пишет:

Конечно иной. Потому и работает.

в:

1вых) спасибо, что тратите свое время и вдохновение :)

2ых) подозреваю, что это решение есть костылем, который работает, но которых стоит избегать

3их) я до сих пор не понял, это я не правильно применяю наследование, или в силу каких-то причин это не так работает, как описывает товарищ Лафоре?

5N62V
Offline
Зарегистрирован: 25.02.2016

sadman41 пишет:
Заменить надо public Parent -> private Public, вроде. Я особо не вникал, но как понимаю - прогонять protected через public бессмысленно, так как идёт понижение "уровня защиты".

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

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Так не покатит? http://cpp.sh/6stlt

// Example program
#include <iostream>
#include <string>
using namespace std;

class Parent{
  protected: 
  float a;
  float b;
  float c;
  
  public:
  
  Parent& operator=(const Parent& src)
  {
      cout << "base\n";
      return *this;
  }

};

class Sub: private Parent{
  protected:
  float d;
  private:

  public:

   Sub& operator=(const Parent& src)
  {
    cout << "derived\n";
    Parent::operator=(src);
    d = 0;   
    return *this;
  }
};

int main()
{
  Parent aaa;
  Sub bbb;
  bbb = aaa;
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

5N62V пишет:
описывает товарищ Лафоре?
Честное слово, не в курсе, что и как описывает тов. Лафоре, но стандарт это описывает примерно так (сильно упрощённо, если нужно точно, то см. раздел 14.4 стандарта, там есть даже пример очень похожий на Ваш - тоже пытаются добраться до protected членов базового класса, указатель на экземпляр которого передан как параметр и получают отлуп - один в один пример, только у Вас ссылка, а там указатель)

Итак, что говорит стандарт: члены производного класса могут добираться к protected членам базового

1. либо через свой указатель this (правильный пример):

class Parent { protected: float a; };

class Sub : public Parent {
	protected: float d;
	public:
	Sub & operator = (const Parent & src) {
		a = 0;	// к этому a добираемся через свой указатель this
					// но это НАШ a, а не src'шный
		return *this;
	}
};

2. либо, когда тип экземпляра определён ОДНОЗНАЧНО как такой же, как тип экземпляра в котором происходит обработка - безо какой бы то ни было вероятности, что он на самом деле преобразован из чего-то ещё (это то, о чём писал rkit). Правильный пример, как можно написать в Вашем случае:

class Parent { protected: float a; };

class Sub : public Parent {
	protected: float d;
	public:
	Sub & operator = (const Parent & src) {
		a = ((Sub *) & src) -> a; // результат - именно то, что Вы хотели сделать
		return *this;
	}
};

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

Что же касается friend, который Вам посоветовали - это не костыль, это "лом против которого нет приёма" / "пушка по воробьям", но не костыль.

Чем плох friend? Тем, что он даёт доступ ко всем (даже private) членам класса Parent всем (а не только оператору присваивания) членам класса Sub. Это несколько неоправданное расширение прав доступа.

rkit
Offline
Зарегистрирован: 23.11.2016

5N62V пишет:

2ых) подозреваю, что это решение есть костылем, который работает, но которых стоит избегать

3их) я до сих пор не понял, это я не правильно применяю наследование, или в силу каких-то причин это не так работает, как описывает товарищ Лафоре?

2. Не переусложняй.

3. Я объяснил, почему оно работает так как работает.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Хотел написать про контекст, но Петрович, как обычно, опередил :)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

DIYMan пишет:

Так не покатит?

Не просто прокатит, а это есть правильное решение - без "ломовости" friend, и без костыля с преобразованием указателя.

Если сравнивать это решение с friend, то можно сказать, что там топор, а здесь скальпель.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

5N62V пишет:

sadman41 пишет:
Заменить надо public Parent -> private Public, вроде. Я особо не вникал, но как понимаю - прогонять protected через public бессмысленно, так как идёт понижение "уровня защиты".

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


Да, я из леса писал, писал и описался: "private Parent", а не "private Public".

rkit
Offline
Зарегистрирован: 23.11.2016

ЕвгенийП пишет:

 "лом против которого нет приёма" / "пушка по воробьям", но не костыль.

Чем плох friend? Тем, что он даёт доступ ко всем (даже private) членам класса Parent всем (а не только оператору присваивания) членам класса Sub. Это несколько неоправданное расширение прав доступа.

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

5N62V
Offline
Зарегистрирован: 25.02.2016

sadman41 пишет:
Да, я из леса писал, писал и описался: "private Parent", а не "private Public".

Да я понял :)

5N62V
Offline
Зарегистрирован: 25.02.2016

1) через friend - как я уже писал, работает, спасибо rkit ! может я неправ, но это уже ближе к зависимости, чем к чистому наследованию.

2) через приведение ссылки на объект, работает, спасибо ЕвгенийП !

//class Sub;

class Parent{
  protected: 
  float a;
  float b;
  float c;
  public:
  Parent(float _a, float _b, float _c):a(_a), b(_b), c(_c) {}
  //friend Sub;

};

class Sub: private Parent{
  protected:
  float d;
  public:
  Sub(float _a, float _b, float _c, float _d):Parent(_a, _b, _c), d(_d) { }

  Sub& operator=(Parent& src){
a = ((Sub *) & src)->a;
b = ((Sub *) & src)->b;
c = ((Sub *) & src)->c;
//d = 0;    
    return *this;
  }

  void getValues(){
    SerialUSB.print(a);SerialUSB.print(" ");SerialUSB.print(b);SerialUSB.print(" ");SerialUSB.print(c);SerialUSB.print(" ");SerialUSB.println(d);
  }
};




void setup() {
SerialUSB.begin(115200);
delay(1000);
}

void loop() {
  Parent aaa(100, 200, 300);
  Sub bbb(0,0,0,400);
  bbb = aaa;
  bbb.getValues();
  while(true);

}

результат: 100.00 200.00 300.00 400.00

3) идея с перегрузкой оператора = в родительском классе вроде бы понятна. Спасибо, DIYMan.но так в лоб не работает

//class Sub;

class Parent{
  protected: 
  float a;
  float b;
  float c;
  public:
  Parent(float _a, float _b, float _c):a(_a), b(_b), c(_c) {}
    Parent& operator=(Parent& src){
    SerialUSB.println("Parent");
    return *this;
  }
  //friend Sub;

};

class Sub: private Parent{
  protected:
  float d;
  public:
  Sub(float _a, float _b, float _c, float _d):Parent(_a, _b, _c), d(_d) { }

  Sub& operator=(Parent& src){
    SerialUSB.println("Sub");
 Parent::operator=(src); 
  //d = 0;    
    return *this;
  }

  void getValues(){
    SerialUSB.print(a);SerialUSB.print(" ");SerialUSB.print(b);SerialUSB.print(" ");SerialUSB.print(c);SerialUSB.print(" ");SerialUSB.println(d);
  }
};




void setup() {
SerialUSB.begin(115200);
delay(1000);
}

void loop() {
  Parent aaa(100, 200, 300);
  Sub bbb(0,0,0,400);
  bbb = aaa;
  bbb.getValues();
  while(true);

}

поля a, b, c остаются равны нулю . Видимо надо что-то делать с объектом родительского класса, возвращаемого в ф-ию перегрузки  дочернего класса. а то он улетает в никуда, как я понял.  К сожалению на сегодня мои опыты закончились: после очередной загрузки скетча в девайс на проце ATSAM3x8e слетел бутлоадер Due,  поэтому он временно окирпичился. :)

5N62V
Offline
Зарегистрирован: 25.02.2016

rkit пишет:

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

Лично я предпочел бы следовать максимально принципу ЧТОБ РАБОТАЛО. Но  по роду деятельности я последнее время общаюсь с программистами, что укрепило меня в моем убеждении, что я ничего не знаю. Но парадокс в том, что у каждого программиста свое личное убеждение что есть по феньшую, и что имеет высший приоритет. И я все больше теряюсь в этих хитрых концепциях и подходах. Поэтому я иногда лишний раз спрашиваю, если что  :)

sadman41
Онлайн
Зарегистрирован: 19.10.2016

5N62V, поставь Codelite, не мучай МК. Тут чистая работа с плюсами - можно все понять быстрее и проще, исполняя код на ПыКа.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

5N62V пишет:

поля a, b, c остаются равны нулю . 

Эммм, а кто будет писать тело оператора присваивания родительского класса? Я там только, пардон, пустой тампон оставил. Вот, всё работает: http://cpp.sh/56ou6 - обратите внимание на тело функции Parent& operator=(const Parent& src) ;)

// Example program
#include <iostream>
#include <string>
using namespace std;

class Parent{
  protected: 
  float a;
  float b;
  float c;
  
  public:
  
  Parent(float _a, float _b, float _c) : a(_a), b(_b), c(_c) {}
  
  Parent& operator=(const Parent& src)
  {
      a = src.a;
      b = src.b;
      c = src.c;
      cout << "base\n";
      return *this;
  }

};

class Sub: public Parent{
  protected:
  float d;
  private:

  public:
  
   Sub(float _a, float _b, float _c, float _d):Parent(_a, _b, _c), d(_d) { }


   Sub& operator=(const Parent& src)
  {
    cout << "derived\n";
    Parent::operator=(src);
  //  d = 0;   
    return *this;
  }
  
  void getValues()
  {
      cout << "a=" << a << "\nb=" << b << "\nc=" << c << "\nd=" << d;
  }
};





int main()
{
  Parent aaa(100, 200, 300);
  Sub bbb(0,0,0,400);
  bbb = aaa;
  bbb.getValues();

}

Выхлоп:

derived
base
a=100
b=200
c=300
d=400 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

5N62V пишет:
идея с перегрузкой оператора = в родительском классе вроде бы понятна. Спасибо, DIYMan.но так в лоб не работает

Parent& operator=(Parent& src){
    SerialUSB.println("Parent");
    return *this;
  }

Здрасьте, девочки! А присваивание кто писать-то будет? У Вас же в операторе только вывод в сериал - он, наверняка работает.

5N62V
Offline
Зарегистрирован: 25.02.2016

Да, у меня вместе с бутлоадером кусочек мозга по-видимому  слетел :)

Работает как надо

class Parent {
  protected:
    float a;
    float b;
    float c;
  public:
    Parent(float _a, float _b, float _c): a(_a), b(_b), c(_c) {}
    Parent& operator = (Parent & src) {
      a = src.a;
      b = src.b;
      c = src.c;
      return * this;
    }
};

class Sub: public Parent {
  protected:
    float d;
  public:

    Sub(float _a, float _b, float _c, float _d): Parent(_a , _b, _c), d(_d) {}

    Sub& operator=(Parent& src) {
      Parent::operator=(src);
      //d = 0;
      return *this;
    }

    void getValues() {
      SerialUSB.print(a); SerialUSB.print(" "); SerialUSB.print(b); SerialUSB.print(" "); SerialUSB.print(c); SerialUSB.print(" "); SerialUSB.println(d);
    }
};

void setup() {
  SerialUSB.begin(115200);
  delay(1500);
}

void loop() {
  Parent aaa(100, 200, 300);
  Sub bbb(0, 0, 0, 400);
  bbb = aaa;
  bbb.getValues();
  while (1);

}

выдает: 100.00 200.00 300.00 400.00

DIYMan, ЕвгенийП - Спасибо!
Че-та в башку не залазит, что из перегрузки родительского класса в перегрузку дочернего возвращается именно родительская часть дочернего объекта. 

DIYMan пишет:

 обратите внимание на тело функции Parent& operator=(const Parent& src) ;)

Не обратил, но работает :)