派生类访问的基类 - 如何?
Posted
技术标签:
【中文标题】派生类访问的基类 - 如何?【英文标题】:Base class to derive class access - How? 【发布时间】:2012-01-18 08:06:38 【问题描述】:如何通过基类对象访问派生类的功能? 我已经编译并运行了程序,没有任何错误(vs2010 express edition)。 任何人都可以澄清这个话题吗?
#include <iostream>
using namespace std;
class A
public:
void f1() cout << " f1 \n";
;
class B : public A
int x;
public:
void f2(int x) this->x = x; cout << "f2 " << this->x <<"\n";
;
int main(int argc, char * argv[])
A * aaa = new A(); // created a base instance
B * b = (B *) aaa; // typecasted it to derived class
b->f2(5); // try to access function, which should not be possible (run-time error?)
return 0;
--> 输出
f2 5 // which concept is supports this output?
【问题讨论】:
【参考方案1】:这是不可能的,因为没有这样的方法。您刚刚调用了 undefined 行为,而您的编译器正在欺骗您。
让我们玩游戏来见证这一点in action:
#include <iostream>
struct A
A(int i = 0): value(i)
int value;
;
struct B: A
B(int i): A(0), other(i)
void set(int i) other = i;
int other;
;
int main(int argc, char* argv[])
A* a = new A[2];
B* b = (B*)a;
b->set(argc);
std::cout << a->value << " " << (a+1)->value << "\n";
std::cout << b->value << " " << b->other << "\n";
输出:
0 1
0 1
哦!数组中的第二个A
怎么改了?
你对编译器撒了谎,它对你撒了谎……程序写到了它不应该写的地方。
【讨论】:
【参考方案2】:您所做的是 C 风格的 upcast,这会导致 undefined 对象内容 - 认为不建议将其与 C++ 一起使用,因为我们有更好的工具在那里可用。请查看 static_cast 和 dynamic_cast - 他们将确保您的演员表能够正常工作。例如,如果你会这样做
B * b = dynamic_cast<B*>(aaa);
你甚至无法编译它,因为 A 不是多态的,即使它是多态的并且类不匹配,它也会返回 NULL 而不是“未定义的东西”。
请注意,动态转换比 static_cast 或 C 样式转换(其行为或多或少类似于 static_cast)要贵一些 - 但由于它们已定义的行为,您可能会考虑至少在调试版本中使用这些:
#ifdef _DEBUG
assert(dynamic_cast<B*>(aaa));
#endif
这可以防止由于未定义的行为(我假设您使用调试版本进行测试)导致的 a) 向上转换和 b) 运行时错误。
【讨论】:
谢谢 Coder02,我知道 C++ 有各种各样的强制转换关键字,但我从没想过我正在执行 C-Style Typecasting。谢谢大家的回复。这是一个很大的帮助:) 别搞错了——C 风格的强制转换并不总是坏事,也并不总是在 C++ 中导致未定义的结果——这只是你的情况,因为 A 不是从 B 继承的。@Anders已经回答了这在内存中的样子以及为什么在这种情况下结果未定义。【参考方案3】:这是未定义的行为(即它是偶然发生的)。
【讨论】:
【参考方案4】:考虑一下:
struct A
int i;
short s;
;
struct B : A
long p;
;
struct A a;
+-----+-----+
| i | s |
+-----+-----+
struct B* b = (B*)&a;
现在访问结构成员 p; (b->p)
您认为 p 具有有效值是否合理?它仍然指向“a”实例。
你应该看看 dynamic_cast 和虚函数
【讨论】:
【参考方案5】:编译实际上将 b->f2 视为 f2(b) 和 f2(b) 等于(伪代码)
address_of_x = b+offset_x_in_B
int x;
memcpy(&x, address_of_x, sizeof(int));
std::cout<<x<<std::endl;
其中 offset_x_in_B 是编译器确定的 c++ 对象模型的值。
所以当 b 是 A 实例时,行为将是未定义的(如果 A 只有一个 int 成员,而不是 x,则应该显示它。)
【讨论】:
以上是关于派生类访问的基类 - 如何?的主要内容,如果未能解决你的问题,请参考以下文章