派生类访问的基类 - 如何?

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-&gt;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,则应该显示它。)

【讨论】:

以上是关于派生类访问的基类 - 如何?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我可以通过指向派生对象的基类指针访问派生私有成员函数?

朋友类对象可以在其成员函数中访问派生类对象上的基类私有成员吗?

无法从派生类构造函数参数访问受保护的基类成员[重复]

如果派生类作为基类返回,如何访问派生类的方法?

protectedpublicprivate

继承中的基类是不是复制到派生类?