尽管派生 class 中有定义,但基 class 中的方法不可见;多态性和使用 `virtual` 关键字

Method nonvisibility in base class despite definition in derived class; polymorphism and using `virtual` keyword

#include <iostream>
class A {
    protected:
        int foo;
};

class B : public A {
    public:
        B(int bar) { foo = bar; }
        int method() { return foo; }
};

class C {
    private:
        A baz;
    public:
        C(A faz) { baz = faz; }
        A get() { return baz; }
};

int main(void) {
    C boo(B(1));
    std::cout << boo.get().method() << std::endl;
    return 0;
}

我有一个基础 class A,其中 B 是 class 的派生。 Class C 采用 A 但我已经通过派生的 class (B) 代替它。没有警告或错误将 B 传递给 C,但我希望在上述情况下具有 method() 的方法可见性。

我对 virtual 不是很熟悉,但我确实尝试将 virtual int method() = 0; 添加到 A,这会导致更多错误。

考虑我是否要添加第二个派生的 class:

class D : public A {
    public:
        D(int bar) { foo = bar; }
        int method() { return foo+1; }
};

我希望 C 能够接受 BD 我最好的假设是接受 A 并让它处理它.

如何以这种方式正确使用多态性?

预期输出如下:

int main(void) {
    C boo(B(1));
    C boz(D(2));
    std::cout << boo.get().method() << std::endl;
    std::cout << boz.get().method() << std::endl;
    return 0;
}

将是:

1
3

如果您需要使用基础 class 类型 A 调用类型 B 的方法 (),则必须在 运行时 期间进行查找。查找是回答以下问题所必需的:应该调用哪个方法? - 与当前行中的类型相对应的那个?还是继承层次结构中的其他方法?” 如果您希望在具有指向 A 的指针或引用时调用 class B 的方法(),则必须创建查找 table。此 table 被称为 vtable(来自虚函数 table),它是通过向函数添加 virtual 关键字来定义的。

#include <iostream>

class A {
    public:
        virtual ~A(){}
        virtual int method() = 0;
    protected:
        int foo;
};

class B : public A {
    public:
        B(int bar) { foo = bar; }
        int method() { 
            std::cout << "Calling method() from B" << std::endl;
            return foo; }
};

class C {
    private:
        A* baz;
    public:
        C(A* faz) { baz = faz; }
        A* get() { return baz; }
};

int main(void) {
    A* element = new B(1);
    C boo(element);
    boo.get()->method();
    return 0;
}

它打印 "Calling method() from B"。请记住,该代码仅用于演示目的,从最佳实践的角度来看并不好。

首先,为了多态地使用A,你需要添加一个virtual析构函数,否则在试图销毁对象时你会运行进入未定义的行为。那么你想通过 A 调用的方法也必须是 virtual。如果它不应该在基础 class 本身中有一个实现,让它成为纯虚拟的:

class A {
    protected:
        int foo;
    public:
        virtual ~A() {}
        virtual int method() = 0;
};

然后在 C 中,您需要使用指向 A 的指针或引用,因为多态性仅适用于这些。

如果您希望 C 拥有 A,正如您的代码示例所建议的,那么您需要提供一个删除指针的析构函数,并且您需要禁用 [=31] 的复制=](或为它决定一些有用的语义):

class C {
    private:
        C(const C&); // Don't allow copying
        C& operator=(const C&); // Don't allow copying
        A* baz;
    public:
        C(A* faz) : baz(faz) { }
        ~C() { delete baz; } 
        A& get() { return *baz; }
};

int main(void) {
    C boo(new B(1));
    C boz(new D(2));
    std::cout << boo.get().method() << std::endl;
    std::cout << boz.get().method() << std::endl;
    return 0;
}

理想情况下,您将升级到 C++11 并使用 std::unique_ptr<A> 而不是 A* 作为成员。但即使你不能这样做,也可以考虑使用 boost::scoped_ptr<A>,它将为你管理删除(你不需要析构函数)并且默认情况下会使 class 不可复制。它还提供更好的异常安全性来将分配封装在这样的智能指针中。