【问题标题】:call a childs version of a function instead of a parents?调用函数的子版本而不是父母?
【发布时间】:2011-08-04 09:42:35
【问题描述】:
好的,所以我有两节课。
class a{
public:
a(){};
void print(){cout << "hello"};
}
class b : public a{
public:
void print(){cout << "hello world";}
}
还有一群带着孩子的父母
a blah[10];
blah[5] = b();
我打电话给 print,并希望它向世界打招呼。
blah[5].print();
但它调用父级。我该如何解决这个问题?
【问题讨论】:
标签:
c++
inheritance
virtual
overriding
【解决方案1】:
将a::print() 声明为虚拟并使用指针/引用 调用print() 函数。当您执行blah[5] = b() 时,它会进行对象切片。使用对象调用虚函数没有任何效果。
【解决方案2】:
这可以通过声明函数 virtual 来解决,比如:
class a{
public:
virtual void print(){
cout << "hello";
}
}
class b : public a{
public:
virtual void print() {
cout << "hello world";
}
}
这就是在 C++ 中实现多态性的方式。更多内容:http://en.wikipedia.org/wiki/Virtual_function
但是,应该注意的是,在您的示例中,它永远不会调用子函数,因为您使用的是对象值,而不是对象的指针/引用。为了解决这个问题,
a * blah[10];
blah[5] = new b();
然后:
blah[5]->print();
【解决方案3】:
这样做是因为您告诉编译器您的实例是a 类型。它在a 对象的数组中,对吗?所以它的类型是a!
当然,您希望b 中的方法覆盖a 中的方法,尽管有父类型的引用。您可以在父类的函数声明中使用 virutal 关键字来获得该行为。
virtual void print(){cout << "hello"};
为什么会这样?
因为当您将对象转换为父类时,您引入了歧义。当这个对象的print()被调用时,我们应该如何对待呢?它的类型为b,但引用的类型为a,因此环绕代码可能期望它的行为类似于a,而不是b!
为了消除歧义,引入了virtual 关键字。 virtual 函数总是被覆盖,如果对象属于包含具有相同签名的方法的子类。
干杯!
【解决方案4】:
您正在寻找的是运行时多态性,这意味着让对象采用“多种形式”(即 a 或 b),并在程序运行时采取相应的行动。在 C++ 中,您可以通过在基类 a 中创建函数 virtual 来做到这一点:
virtual void print() {cout << "hello"};
然后,您需要通过指针或引用来存储元素,并且 - 通常派生类可以引入新的数据成员并需要更多存储空间 - 在堆上使用 new 创建对象是正常的:
a* blah[10];
blah[5] = new b();
然后你可以调用:
blah[5]->print();
它会调用print()的b实现。
你应该稍后delete blah[5](以及你指向new返回的内存的任何其他人)。
在实践中,最好使用一个容器,该容器可以在其自身被破坏时删除它所包含的对象,无论是由于离开范围还是被删除。 std::vector<> 就是这样一个容器。您还可以使用智能指针自动删除a 和b 对象。如果在您的 delete 语句执行之前抛出异常,并且您希望程序继续运行而不会泄漏内存,这有助于使代码正确。 boost 库是从中获得智能指针实现的最简单/最佳的地方。一起:
#include <vector>
#include <boost/shared_ptr.hpp>
std::vector<boost::shared_pointer<a> > blah(10);
blah[5] = new b();
(将向量与push_back() 一起使用更为正常,因为它会自动增大向量以适应您添加的所有元素,并通过调用vector::size() 获得新的总数。)