颜林林的个人网站

Linlin Yan's Personal Website

多态对象列表

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++ 编译器的标准支持!