多态对象列表
2009-07-05 17:02
之所以C++有时不被当作高级语言,那是因为它是一门对变量内存布局很讲究的语言,每个变量所占用的空间大小如果发生变化,有时候是会影响程序的结果的。例如在同时使用多态对象和数组技术时,就容易出现这样的问题。
我们首先来看看高级语言C#写的一段小程序:
using System;
using System.Collections.Generic;
namespace Test
{
class Base
{
public virtual void Foo() { Console.WriteLine("From Base"); }
}
class Derived : Base
{
public override void Foo() { Console.WriteLine("From Derived"); }
}
class Program
{
static void Main(string[] args)
{
List<Base> a = new List<Base>();
a.Add(new Base());
a.Add(new Derived());
foreach (Base b in a)
{
b.Foo();
}
}
}
}
程序的执行结果是:
From Base
From Derived
很简单的程序,把基类和派生类分别实例化出来的对象放入列表中,然后逐个取出来。结果也如同我们所预料的那样,分别执行了基类和派生类的成员函数(方法)。然而到了C++中,却没有那么理想了:
#include <iostream>
#include <vector>
class Base
{
public:
virtual void Foo() { std::cout << "From Base" << std::endl; }
};
class Derived : public Base
{
public:
virtual void Foo() { std::cout << "From Derived" << std::endl; }
};
int main()
{
std::vector<Base> a;
a.push_back(Base());
a.push_back(Derived());
for (size_t i = 0; i < a.size(); i++)
{
a[i].Foo();
}
return 0;
}
程序的执行结果是:
From Base
From Base
原来,被放入列表后,派生类对象也被“截断”成为基类对象,因而只能调用到基类的成员函数了。这就是C++中一个很重要的原则:**想要实现多态,必须使用指针或引用!**这在《More Effective C++》中也有详细地阐述(“条款3:不要使用多态性数组”)。然而,引用是无法放入列表容器中的,因此只能使用对象指针的数组:
// ...
// Base 和 Derived 类定义同前
// ...
int main()
{
std::vector<Base*> a;
a.push_back(new Base());
a.push_back(new Derived());
for (size_t i = 0; i < a.size(); i++)
{
a[i]->Foo();
}
for (size_t i = 0; i < a.size(); i++)
{
delete a[i];
}
return 0;
}
这时结果就符合我们的预期了:
From Base
From Derived
但比较麻烦的是,我们必须在结束使用这些 new 出来的对象时,用 delete 逐个销毁它们。所幸,boost 中有一利器:boost::shared_ptr
,能帮我们分担点这方面的焦虑:
// ...
#include <boost/shared_ptr.hpp>
// ...
// Base 和 Derived 类定义同前
// ...
int main()
{
std::vector<boost::shared_ptr<Base>> a;
a.push_back(boost::shared_ptr<Base>(new Base()));
a.push_back(boost::shared_ptr<Base>(new Derived()));
for (size_t i = 0; i < a.size(); i++)
{
a[i]->Foo();
}
return 0;
}
所以,强烈期盼 boost 早日成为众多 C++ 编译器的标准支持!