派生类中具有相同名称的成员变量



UPDATE:行为不是特定于模板的,所以

struct Derived : public Base {
OverrideFoo* m_data {new OverrideFoo()};
}

所以似乎派生中的m_data和 Base 中的m_data都存在于内存布局中。如果我们在派生中定义一个函数, 例如,Derived::p rint() { m_data->print()},这将使用 Derived 中的m_data,但是,如果在派生对象上调用 base 函数,它仍然使用 Base 中的 m_data。

我对以下代码的行为感到惊讶,它打印的是"from foo",而不是"from override foo"。 为什么会这样? "m_data"不应该是"OverrideFoo"的类型吗?

#include <iostream>
using namespace std;
struct Foo {
void print() {
printf("from foon");
}
};
struct OverrideFoo {
void print() {
printf("from override foon");   
}
};
struct Base {
void useData() {
m_data->print();
}
Foo* m_data {new Foo()};  
};
template <class t> 
struct Derived : public Base {
t* m_data {new t()};
};
int main()
{
Derived<OverrideFoo> d;
d.useData();
return 0;
}

当你调用d.useData()时,你正在调用Base::useData(),它访问Base::m_data

我想你希望Base::useData()使用Derived::m_data,只是因为变量具有相似的名称。然而,这不是这样工作的。这两个类都有自己独立的m_data,并且在您的情况下,具有不同的类型。

确实,Derived::m_data隐藏Base::m_data,这可能会向您暗示它们是相关的,或者一个"覆盖"另一个。不要将其与隐藏混为一谈。隐藏是类似命名的自然结果。如果Derived需要访问Base::m_data,它必须限定它才能消除与自己的m_data的歧义。

注意:成员变量/字段不能被覆盖。如果需要覆盖样式的行为,则需要通过成员函数(类似于virtual IPrintable* GetPrintable().基类必须授予使用virtual关键字重写的可能性。

另一种思考方式:Base,尽管它的名字暗示,是一个完整的类型。您可以执行Base x;来实例化和使用此类,而无需派生。编译器为Base生成完整且功能正常的代码,包括访问Base::m_data的代码。如果m_data以某种方式被覆盖,如何生成此代码?如果Base的数据类型可以在某个基类中被覆盖,sizeof(*m_data)会是什么理解?编译器怎么知道m_data甚至指的是什么,如果你建议它可以由派生它的任何类更改?

另一点:如果成员能够在默认情况下被覆盖(没有virtual关键字),这将导致基类的大规模混乱。想象一下,编写一个泛型基类并冒着派生类可能会在不知不觉中更改基类状态的风险?或者想象一下,编写一个派生类并担心你的变量命名,因为"也许基类使用相同的名称?

那么让我们总结一下关键点:

  1. 字段不能被覆盖,句点。它会打破许多其他事情(整个其他主题)中的sizeof()
  2. 基类必须显式授予派生类以通过virtual关键字重写成员函数。

不过,可能有更好的方法来做你正在尝试的事情。对我来说最自然的是将Foo类型指定为模板参数以Base

喜欢这个:

struct Foo1 {
void print() {
printf("from foon");
}
};
struct Foo2 {
void print() {
printf("from override foon");   
}
};
template<typename TData>
struct Base {
void useData() {
m_data.print();
}
TData m_data;
};
template <typename TData> 
struct Derived : public Base<TData> {
};
int main()
{
Derived<Foo1> d1;
d1.useData();
Derived<Foo2> d2;
d2.useData();
return 0;
}

很难知道最适合您的方法,因为这是一个不切实际的人为示例。

尝试这段代码,你会发现这两个m_data具有不同的内存地址,这意味着它们是不同的变量。

#include <iostream>
using namespace std;
struct Foo {
void print() {
printf("from foon");
}
};
struct OverrideFoo {
void print() {
printf("from override foon");   
}
};
struct Base {
void useData() {
m_data->print();
std::cout << m_data << std::endl;
}
Foo* m_data {new Foo()};  
};
template <class t> 
struct Derived : public Base {
t* m_data {new t()};
};
int main()
{
Derived<OverrideFoo> d;
d.useData();
d.m_data->print();
std::cout << d.m_data << std::endl;
return 0;
}

最新更新