#Inheritance rules

24 messages · Page 1 of 1 (latest)

pale sinew
#

I understand that when you inherit a parent class, the constructor of the child class should call the parent class constructor and that it shouldn’t just initialize the members that the parent class is supposed to initialize. It’s still acceptable to have the child constructor initialize/ set values for parent class members that aren’t touched in the parent class constructor right?

mighty scrollBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

agile viper
#

generally you want a constructor to initialize all the members, so there shouldn't really be any members in the parent class that aren't touched by the constructor

#

that being said, it can be fine to just not call a constructor and initialize the members yourself in the base class, though that means that the base class members need to be protected or public

#

you should try to not build deep inheritance trees, ideally your base classes are all "interfaces" in the sense that they don't contain any nonstatic member variables

frigid surge
#

Technically you cannot initialize members of the base class in the derived class' constructor. By the time the derived class' constructor runs, the base class' constructor already ran and created all the members. All you can do now is overwrite their values.

#

Overwriting the members of the base class is a reasonable thing to do for a derived class, but you should probably use a constructor of the base class for that so you can directly initialize those variables instead of constructing them with the wrong value and overwriting it after. Maybe the compiler can fix that, maybe not, but it's just cleaner to give things the right value from the start.

agile viper
#

yeah I guess what I said was really misleading

#

I meant something like ```c++
struct Base {
int x;
int y;
};
// ...
Derived::Derived(int x, int y): Base {x, y}

where you haven't written a `Base` constructor yourself
autumn blaze
# pale sinew I understand that when you inherit a parent class, the constructor of the child ...

when an object of the child class is created, the child class will always initialize the base class with or without you giving it the data to initialize. If not given, it'll be initialized with default value which later you can overwrite in your child class, as Toeger said, which costs additional time unnecessarily while you can give it the data to initialize at the start, I want you to remember that, the base class is initialized before the child class, even when you're creating the object of the child class

#

;compile ```cpp
#include <iostream>
class Base {
public:
int a;
Base(int x = 10) : a(x) {
std::cout << "Base construct" << std::endl;
}
};

class Der : public Base {
public:
Der() {
std::cout << "Der construct" << std::endl;
}
void print() {
std::cout << a;
}
} ;

int main() {
Der D;
D.print();
return 0;
}

crisp pumiceBOT
#
Program Output
Base construct
Der construct
10
autumn blaze
#

now compare the asm of the code

#

;asm ```cpp
#include <iostream>
class Base {
public:
int a;
Base(int x = 10) : a(x) {
std::cout << "Base construct" << std::endl;
}
};

class Der : public Base {
public:
Der() {
std::cout << "Der construct" << std::endl;
}
void print() {
std::cout << a;
}
} ;

int main() {
Der D;
D.print();
return 0;
}

crisp pumiceBOT
#
Assembly Output Pt. 1
.LC0:
  .string "Base construct"
Base::Base(int) [base object constructor]:
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov QWORD PTR [rbp-8], rdi
  mov DWORD PTR [rbp-12], esi
  mov rax, QWORD PTR [rbp-8]
  mov edx, DWORD PTR [rbp-12]
  mov DWORD PTR [rax], edx
  mov esi, OFFSET FLAT:.LC0
  mov edi, OFFSET FLAT:std::cout
  call std::basic_ostream<char, std::char_traits<char>>& std::operator<<<std::char_traits<char>>(std::basic_ostream<char, std::char_traits<char>>&, char const*)
  mov esi, OFFSET FLAT:std::basic_ostream<char, std::char_traits<char>>& std::endl<char, std::char_traits<char>>(std::basic_ostream<char, std::char_traits<char>>&)
  mov rdi, rax
  call std::ostream::operator<<(std::ostream& (*)(std::ostream&))
  nop
  leave
  ret
.LC1:
  .string "Der construct"
Der::Der() [base object constructor]:
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov QWORD PTR [rbp-8], rdi
  mov rax, QWORD PTR [rbp-8]
  mov esi, 10
  mov rdi, rax
  call Base::Base(int) [base object constructor]

Assembly Output Pt. 2
  mov esi, OFFSET FLAT:.LC1
  mov edi, OFFSET FLAT:std::cout
  call std::basic_ostream<char, std::char_traits<char>>& std::operator<<<std::char_traits<char>>(std::basic_ostream<char, std::char_traits<char>>&, char const*)
  mov esi, OFFSET FLAT:std::basic_ostream<char, std::char_traits<char>>& std::endl<char, std::char_traits<char>>(std::basic_ostream<char, std::char_traits<char>>&)
  mov rdi, rax
  call std::ostream::operator<<(std::ostream& (*)(std::ostream&))
  nop
  leave
  ret
Der::print():
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov QWORD PTR [rbp-8], rdi
  mov rax, QWORD PTR [rbp-8]
  mov eax, DWORD PTR [rax]
  mov esi, eax
  mov edi, OFFSET FLAT:std::cout
  call std::ostream::operator<<(int)
  nop
  leave
  ret
main:
  push rbp
  mov rbp, rsp
  sub rsp, 16
  lea rax, [rbp-4]
  mov rdi, rax
  call Der::Der() [complete object constructor]
  lea rax, [rbp-4]
  mov rdi, rax
  call Der::print()
  mov eax, 0
  leave
  ret

autumn blaze
#

in the .LC1 text segment, you can see this line where it calls

call Base::Base(int)
#

before the std::ostream call which is std::cout

#

so you can't do anything about that unless you hide the base class' constructor but you can't do that either

#

if you hide the base class' constructor, this is what happens

#

;compile ```cpp
#include <iostream>
class Base {
public:
int a;
private:
Base(int x = 10) : a(x) {
std::cout << "Base construct" << std::endl;
}
};

class Der : Base {
public:
Der() {
std::cout << "Der construct" << std::endl;
}
void print() {
std::cout << a;
}
} ;

int main() {
Der D;
D.print();
return 0;
}

crisp pumiceBOT
#
Compiler Output
<source>: In constructor 'Der::Der()':
<source>:13:15: error: 'Base::Base(int)' is private within this context
   13 |         Der() {
      |               ^
<source>:6:9: note: declared private here
    6 |         Base(int x = 10) : a(x) {
      |         ^~~~
Build failed
autumn blaze
#

^

#

hope you understand now :)

#

that's far more information than you asked for ig 😅