2

假设我们有一个类或类NSArray的实例的 Objective-C 。这两个类都有方法。ShoeComputerage

Objective-C 将允许您通过一个简单的语句来获取所有鞋子和计算机的年龄,如下所示:

NSArray *agesOfShoesAndComputers = [ourArray valueForKey:@"age"];

C++ 是否有任何类似的方法可以应用于向量,或者像这样的东西在 C++ 中总是会更麻烦一些?

4

3 回答 3

6

首先,std::vector<T>静态类型的。也就是说,您需要在编译时知道它是否包含鞋子或计算机。

所以,简短的回答是它在 C++ 中总是比较麻烦(静态类型确实是一种优化,所以这是我们为性能付出的代价)。


如果你想使用传统的运行时多态,你需要一个通用的基类:

class MyBase
{
public:
    virtual ~MyBase();
    virtual int age() = 0;
};

class Shoe: public MyBase
{
public:
    virtual int age();
};
// same for Computer

现在我可以拥有

std::vector<std::unique_ptr<MyBase>> ourArray;

std::vector<int> ages(std::vector<std::unique_ptr<MyBase>> const &ourArray)
{
    std::vector<int> ages;
    std::transform(ourArray.begin(), ourArray.end(),
                   std::back_inserter<int>(ages),
                   [](std::unique_ptr<MyBase> const& elem) { return elem->age(); }
                  );
    return ages;
}

对此处的调用elem->age()依赖于 定义的公共接口MyBase,但分派到派生类实现(Shoe::age()Computer::age()分别)。

它不如 Objective C 版本灵活,因为你的通用接口、继承等都是静态定义的。

请注意,如果这是一个简单的属性,您可以完全跳过虚拟方法调用并将其放入MyBase。Shoes 和 Computers 仍然需要从这个类继承,但它们可以只在基类中设置属性值。


您也许可以使用编译时多态性,即模板。这在某些方面更类似于 Objective C 方法,因为它适用于支持预期方法的任何类型,而与继承关系无关。

template <typename T> // T could be Shoe, Computer or anything else
std::vector<int> ages(std::vector<T> const &ourArray)
{
    std::vector<int> ages;
    std::transform(ourArray.begin(), ourArray.end(),
                   std::back_inserter<int>(ages),
                   [](T const& elem) { return elem.age(); }
                  );
    return ages;
}

但是,该机制完全不同-您仍然有静态不同的呼叫站点,无论是 vector<Shoe> vector<Computer>还是其他,并且永远不会相遇。

于 2012-08-16T16:14:36.313 回答
2

C++ 的动态性稍差一些,但您要寻找的通常是transform

vector<unique_ptr<BaseClassForShoeAndComputer>> ourArray;
vector<int> agesOfShoesAndComputers;

transform(ourArray.begin(), ourArray.end(),
          back_inserter(agesOfShoesAndComputers),
          [](unique_ptr<BaseClassForShoeAndComputer>& s) { return s->age(); });

但是,您不能在单个容器中真正拥有不相关的对象(嗯,您可以,但使用它们是另一回事);但是,您可以使用模板(不幸的是,不能使用 lambdas)来实现静态鸭子类型(用于具有“年龄”方法的任何类型)。

于 2012-08-16T16:07:03.937 回答
2

如果您的编译器不支持lambdas您可以使用<functional>(但无论如何都是 c++ 11)

std::vector<std::shared_ptr<Base>> v;

std::vector<int> ages;
std::transform(v.begin(),
               v.end(),
               std::back_inserter(ages),
               std::bind(&Base::getAge, std::placeholders::_1));

Base的基类在哪里ShoeComputer

您还可以使用模板从任何支持getAge()函数的类型中获取年龄。

template <typename InputIterator>
static std::vector<int> getAges(InputIterator start, InputIterator finish)
{
    typedef typename InputIterator::value_type T;

    std::vector<int> ages;
    std::transform(start,
                   finish,
                   std::back_inserter(ages),
                   std::bind(&T::getAge, std::placeholders::_1));
    return ages;
}

int main()
{
    std::vector<Base> v;
    std::vector<int> ages = getAges(v.begin(), v.end());
}
于 2012-08-16T16:09:12.597 回答