首页 > 解决方案 > 如何正确处理继承(方法和指针)?

问题描述

我和我的朋友正在讨论继承问题,无法弄清楚我们做错了什么。

我们有继承结构A-> B->DC-> 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;
}

其他我们不明白的地方:

  1. 例如,调用b.num()时没有问题,但是调用时b.name(),我们需要标有箭头和字母B的行。如果它是继承自返回A类型A*,为什么它也不能返回B*派生类?

  2. 做一些像B* name (std::string str) { m_name = str; return this; }好的练习一样吗?我们认为它不是,但由于类成员和方法的数量非常多,它确实缩短了我们实际项目中的内容。也许我们可以使用对对象的引用,而不是指向类的指针B& name (std::string str) { m_name = str; return *this; }

  3. 我们如何处理更复杂的继承结构而不会像现在这样遇到冲突?例如:

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

非常感谢有关此混乱代码的其他提示。

标签: c++classpointersinheritancereference

解决方案


对于第一个示例,如果您的调用b.name("bob")将返回一个指向类 A 实例的指针。然后您获取该实例并调用该num()函数。A类没有num()功能。那是在派生类 B 中添加的东西。

对于问题 1 和 2:

您提到 name() 在 B 类中从 A 类继承,但不是您想的那样。在 A 类中,您需要将 name() 函数指定为virtual。这意味着 B 类将能够通过覆盖它来更改函数的实现。目前 B 类正在创建自己的 name() 函数,该函数与 A 类中的 name() 函数无关。

完成此操作后,您可以使用协变返回类型将返回类型更改为派生类,这应该解决 1 和 2。

对于第三个问题:

您在这里面临的问题通常被称为钻石问题,它本身就是一个很大的话题。有办法解决它。看看这篇文章和这篇文章。

其他一些提示:

  1. 如果您使用继承,请确保您了解virtualoverride关键字的使用。

  2. 查看一些成员函数的const关键字。这表明函数不会改变类本身。这应该用于所有不改变类的成员函数。您的代码中的示例是 name() 和 num() 的“getter”函数。这被称为const 正确


推荐阅读