My friend and I are going over inheritance and can't figure out what we're doing incorrectly.
We have the inheritance structure A
->B
->D
and C
->D
.
In this shorter example, we are wondering
1 - Why won't this code work without the lines marked A and B?
#include <iostream>
#include <string>
class A {
protected:
std::string m_name;
public:
std::string name() { return m_name; }
A* name (std::string str) { m_name = str; return this; }
};
class B : public A {
protected:
int m_num;
public:
std::string name() { return m_name; } // <--- A
int num() { return m_num; }
B* name (std::string str) { m_name = str; return this; } // <--- B
B* num (int x) { m_num = x; return this; }
};
int main() {
A a;
B b;
a.name("alice");
b.name("bob")->num(10);
std::cout << a.name() << " " << b.name() << " " << b.num() << std::endl;
return 0;
}
In this longer example, we're wondering why the following 5 errors are popping up, because it's preventing us from progressing to our goal.
2 - How can we inherit what we need here?
#include <iostream>
#include <string>
class A {
protected:
std::string m_name;
public:
A() = default;
A(std::string str) : m_name(str) { }
A(const A& a) = default;
std::string name() { return m_name; }
A* name (std::string str) { m_name = str; return this; }
};
class B : public A {
protected:
int m_num;
public:
B() = default;
B(std::string str, int x)
: m_name(str) // 1. class 'B' does not have any field named 'm_name'
, m_num(x) { }
B(const B& b) = default;
std::string name() { return m_name; } // <--- A
int num() { return m_num; }
B* name (std::string str) { m_name = str; return this; } // <--- B
B* num (int x) { m_num = x; return this; }
};
class C {
protected:
std::string m_name; //same as in A
unsigned long m_bigNumber;
public:
C() = default;
C(std::string str, unsigned long big) : m_name(str), m_bigNumber(big) { }
C(const C& c) = default;
std::string name() { return m_name; }
unsigned long bigNumber() { return m_bigNumber; }
C* name (std::string str) { m_name = str; return this; }
C* bigNumber (unsigned long big) { m_bigNumber = big; return this; }
};
class D : public B , public C {
private:
std::string m_thing;
public:
D() = default;
D(std::string str1, std::string str2, int x, unsigned long big)
: m_thing(str1)
, m_name(str2) // 2. request for member 'm_name' is ambiguous
, m_num(x) // 3. class 'D' does not have any field named 'm_num'
, m_bigNumber(big) { }
D(const D& d) = default;
std::string thing() { return m_thing; }
D* thing (std::string str) { m_thing = str; }
};
int main() {
A a("alpha");
B b("bravo", 100);
C c("cookie", 1000);
D d("ddddd thing", "davis", 123, 123123);
a.name("alice");
b.name("bob")->num(10);
c.name("charlie")->bigNumber(123456789);
d.name("delta")->thing("delta thing")->num(200)->bigNumber(123456); //
// 4. request for member 'name' is ambiguous
std::cout << a.name() << " " << b.name() << " " << b.num() << std::endl;
std::cout << c.name() << " " << c.bigNumber() << std::endl;
std::cout << d.name() << " " << d.num() << " "; //
// 5. same as error 4
std::cout << d.thing() << " " << d.bigNumber() << std::endl;
return 0;
}
Other things that we don't understand:
When we call, e.g.,
b.num()
, there is no problem, but when we callb.name()
, we need the line marked with the arrow and letter B. If it was inherited fromA
with return typeA*
, why would it not also be able to returnB*
if it is a derived class?Is doing something like
B* name (std::string str) { m_name = str; return this; }
even good practice? We feel that it is not but it really shortens stuff in our actual project due to the extremely high number of class members and methods. Perhaps we can use, instead of a pointer to the class, a reference to the object withB& name (std::string str) { m_name = str; return *this; }
?How could we handle a more complex inheritance structure without running into conflicts like we are here? For example:
class A { /* ... */ };
class B : public A { /* ... */ };
class C : public A { /* ... */ };
class D : public B, public C { /* ... */ }; // therefore has functionality of A, B, C
class E : public C { /* ... */ }; // therefore has functionality of A, C, but not B
class F : public A, public E { /* ... */ }; therefore has functionality of A, C, E, but not B
Other tips on this messy code would be greatly appreciated.