c++ - 如何正确处理继承(方法和指针)?
问题描述
我和我的朋友正在讨论继承问题,无法弄清楚我们做错了什么。
我们有继承结构A
-> B
->D
和C
-> D
。
在这个较短的例子中,我们想知道
1 - 为什么没有标记 A 和 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;
}
在这个更长的示例中,我们想知道为什么会弹出以下 5 个错误,因为它阻止了我们朝着目标前进。
2 - 我们如何继承我们需要的东西?
#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;
}
其他我们不明白的地方:
例如,调用
b.num()
时没有问题,但是调用时b.name()
,我们需要标有箭头和字母B的行。如果它是继承自返回A
类型A*
,为什么它也不能返回B*
派生类?做一些像
B* name (std::string str) { m_name = str; return this; }
好的练习一样吗?我们认为它不是,但由于类成员和方法的数量非常多,它确实缩短了我们实际项目中的内容。也许我们可以使用对对象的引用,而不是指向类的指针B& name (std::string str) { m_name = str; return *this; }
?我们如何处理更复杂的继承结构而不会像现在这样遇到冲突?例如:
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
非常感谢有关此混乱代码的其他提示。
解决方案
对于第一个示例,如果您的调用b.name("bob")
将返回一个指向类 A 实例的指针。然后您获取该实例并调用该num()
函数。A类没有num()
功能。那是在派生类 B 中添加的东西。
对于问题 1 和 2:
您提到 name() 在 B 类中从 A 类继承,但不是您想的那样。在 A 类中,您需要将 name() 函数指定为virtual。这意味着 B 类将能够通过覆盖它来更改函数的实现。目前 B 类正在创建自己的 name() 函数,该函数与 A 类中的 name() 函数无关。
完成此操作后,您可以使用协变返回类型将返回类型更改为派生类,这应该解决 1 和 2。
对于第三个问题:
您在这里面临的问题通常被称为钻石问题,它本身就是一个很大的话题。有办法解决它。看看这篇文章和这篇文章。
其他一些提示:
查看一些成员函数的const关键字。这表明函数不会改变类本身。这应该用于所有不改变类的成员函数。您的代码中的示例是 name() 和 num() 的“getter”函数。这被称为const 正确。
推荐阅读
- javascript - 数据到 av 模型?如果数据来自另一个组件,我是否必须初始化数据?
- nim-lang - Nim中的字符串枚举类型?
- wpf - 从 WPF/PowerShell 中的数据网格上下文菜单中获取单击的 MenuItem 标头值
- java - android服务始终运行并在需要时通知用户
- javascript - 实时搜索对象输入 vanilla JS
- javascript - Jquery 得到现在和未来日期之间的不正确差异
- asp.net-core - Elastic APM 显示在 .Net Core API 端点上执行的 SQL 查询总数
- java - 使用休眠更新记录的正确方法是什么,更改不会反映在数据库中?
- java - How to use google maps android SDK in a fragment?
- android - android中片段的Twitter授权问题