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

时间:2012-01-18 08:06:39

标签: c++

如何通过基类对象访问派生类的函数? 我编译并运行程序没有任何错误(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;
}

- &GT;输出

f2 5   // which concept is supports this output?

5 个答案:

答案 0 :(得分:3)

这是不可能的,因为没有这样的方法。您刚刚调用了 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发生了变化?

你已经骗了编译器,它骗了你......程序写了它应该没有的地方。

答案 1 :(得分:1)

这是未定义的行为(即它偶然起作用)。

答案 2 :(得分:1)

考虑一下:

struct A
{
   int i;
   short s;
};

struct B : A
{
   long p;
};

struct A a;

+-----+-----+
|  i  |  s  |
+-----+-----+

struct B* b = (B*)&a;

现在访问struct member p; (b->p)

你觉得p有效值似乎是合理的吗?它仍然指向'a'实例。

你应该看一下dynamic_cast和虚函数

答案 3 :(得分:1)

编译实际上将b-&gt; 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,则应该显示它。)

答案 4 :(得分:1)

你做的是C风格的 upcast ,导致未定义的对象内容 - 考虑不建议将它与C ++一起使用,因为我们有更好的工具在那里。请查看static_cast&lt;&gt;和dynamic_cast&lt;&gt; - 他们会确保你的演员阵容能够奏效。例如,如果你已经完成了

B * b = dynamic_cast<B*>(aaa);

你甚至无法编译它,因为A不是多态的,即使它和类不匹配,它也会返回NULL而不是“未定义的东西”。

请注意,动态转换比static_cast或C风格的转换(它的行为或多或少类似于static_casts)要贵一些 - 但由于它们的定义行为,您可能会考虑至少在调试版本中使用这些:

#ifdef _DEBUG
    assert(dynamic_cast<B*>(aaa));
#endif

这可以防止a)upcast和b)由于未定义的行为导致的运行时错误(我假设您使用调试版本进行测试)。