15

我在下面有一个代码片段:

#include <iostream>

using namespace std;

class Base {
public:
    Base() : b(0) {}
    int get();
    virtual void sayhello() { cout << "Hello from Base with b: " << b << endl; }
private:
    int b;
};

int Base::get() {sayhello(); return b;} 

class Derived : public Base {
public:
    Derived(double b_):b(b_){}
    void sayhello() { cout << "Hello from Derived with b: " << b << endl; }
private:
    double b;
};

int main() {
    Derived d(10.0);
    Base b = d;

    cout << "Derived b: " << d.get() << endl;
    cout << "Base b: " << b.get() << endl;
}

运行编译后的可执行文件,我发现结果在我的llvm-g++ 4.2机器上超出了我的预期。我的盒子上的输出是

Hello from Derived with b: 10
Derived b: 0
Hello from Base with b: 0
Base b: 0

我想要在代码中做的是覆盖类b中的成员字段()Derived。由于我认为两者都Base需要Derived访问该字段,所以我在 中定义了一个get成员函数Base,因此Derived可以继承它。然后我尝试从不同的对象中获取成员字段。

结果表明我仍然得到原始b的 in Basebyd.get()而不是 in Derived,这是我期望代码做的。代码(或我的理解)有什么问题吗?规范中是否指定了此行为?覆盖成员字段并正确定义其 getter 和 setter 的正确方法是什么?

4

4 回答 4

19

派生类中添加的新内容b不会覆盖基类的b. 它只是隐藏它。

因此,在派生类中有两个b,并且虚拟方法打印相应的b.

于 2013-10-10T08:38:41.330 回答
10

您不能简单地覆盖成员字段,并且在Base::get编译时,b变量被解析为,Base::b因此此方法将始终使用此值,而不是派生类中具有相同名称的另一个字段的值。

覆盖属性的常用方法是覆盖访问它的方式,即覆盖访问器(getter 和 setter)。

您可以通过装饰 getter 来实现类似的效果,但 getter 返回类型将始终相同:

class Base {
public:
    Base() : b(0) {}
    int get();
    virtual void sayhello() { cout << "Hello from Base with b: " << b << endl; }
protected:
    virtual int getB() {return b;}
private:
    int b;
};

int Base::get() {sayhello(); return getB();} 

class Derived : public Base {
public:
    Derived(double b_):b(b_){}
    void sayhello() { cout << "Hello from Derived with b: " << b << endl; }
protected:
    int getB() override {return b;} // conversion from double to int
private:
    double b;
};
于 2013-10-10T08:39:05.760 回答
3

我不确定我是否正确理解了您,但是“覆盖”您的意思是“替换”,您将使用模板:

#include <iostream>
using namespace std;

template< typename T >
class Base {
public:
    Base() : b(0) {}
    Base(T b_) : b(b_) {}
    T get();
    virtual void sayhello() { cout << "Hello from Base with b: " << b << endl; }
protected:
    T b;
};

template< typename T >
T Base<T>::get() {sayhello(); return b;} 

class Derived : public Base<double> {
public:
    Derived(double b_):Base(b_){}
    void sayhello() { cout << "Hello from Derived with b: " << this->b << endl; }
};

int main() {
    Derived d(10.0);
    Base<double>* b = &d;

    cout << "Derived b: " << d.get() << endl;
    cout << "Base b: " << b->get() << endl;
}

您的代码main也在尝试Base b = d;这会导致切片,上面修复了该问题并确保您不会意外使用Base<int>而不是Base<double>.

活生生的例子

于 2013-10-10T08:43:05.873 回答
2

你应该重写你的 Derived::ctor 如下:

Derived(double _b)
:Base(_b)
{}

并删除b派生类中的归档。b而是在类中标记Base为受保护。

编辑
忽略所有这些我在您的代码中发现了一个问题:

Base b = d;

您正在将派生对象复制到基础。它只复制基本字段。如果您想要多态性,请尝试下一个:

Base *b = &d;
b->get()
于 2013-10-10T08:47:09.653 回答