首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >标准容器的C++矛盾问题

标准容器的C++矛盾问题
EN

Stack Overflow用户
提问于 2019-05-10 00:25:17
回答 1查看 94关注 0票数 0

我和我的同事正在为我们的代码库实现Google Test,并且遇到了一些关于使用标准模板容器的Contravariance问题。

因此,Google Test要求我们创建一个纯虚拟接口类,它反映了我们的实际类,它将继承接口并实现所有虚拟函数。这将在Google Mock中用于测试。这也是一个严格的工作要求,否则我们需要将模板添加到我们的所有类中,这些类将只有一种类型……仅仅是为了让测试代码正常工作,这似乎是相当不直观的。

因此,我们只调查了一些代码,这些代码显示了问题的行为:

代码语言:javascript
复制
#include <vector>
#include <string>
#include <iostream>

class Fruit{
public:
    Fruit(std::string colorIn) : color(colorIn) {}
    std::string color;
};

class Apple : public Fruit{
public:
    Apple() :  Fruit("Red"){ appleType = "Honey Crisp"; }
    Apple(const Fruit& fruit) : Fruit(fruit.color) { appleType = "Honey Crisp"; }
    std::string appleType;
};

class Banana : public Fruit{
public:
    Banana() :  Fruit("Yellow"){ bananaType = "Dole"; }
    Banana(const Fruit& fruit) : Fruit(fruit.color) { bananaType = "Dole"; }
    std::string bananaType;
};

void takeMyFruit(std::vector<Fruit>& fruits){
    if(!fruits.empty()){
        std::cout << "Take my " << fruits[0].color << " " << ((Banana)(fruits[0])).bananaType << " banana." << std::endl;
        std::cout << "Take my " << fruits[1].color << " " << ((Apple)(fruits[1])).appleType << " apple." << std::endl;
    }else{
        std::cout << "You gave me an empty bag?" << std::endl;
    }
}

int main(){
    std::vector<Fruit> fruits;
    fruits.push_back(Banana());
    fruits.push_back(Apple());

    std::vector<Banana> bananas = { Banana() };

    std::vector<Apple> apples = { Apple() };

    takeMyFruit(fruits);    //Why can I do this?
    //takeMyFruit(bananas); //Compile error due to contravariance
    //takeMyFruit(apples);  //Compile error due to contravariance

    return 0;
}

我们需要能够编译一些可以接受容器的基类型的东西,例如std::vector<BaseType>,但我们只用一个DerivedType填充它。

为什么我们可以在上面创建的代码示例中的std::vector<Fruit>中混合使用两种不同的派生类型(即AppleBanana),但不能将std::vector<DerivedType>传递给接受std::vector<BaseType>的函数参数

关于Google Test和Google Mock,最好的解决方法是什么?他们说,如果要更改生产代码以适应测试的需要,那么这可能不是最佳实践。

我们看到的另一种方法是将派生类型的模板添加到任何将其定义为成员的类中。这样做将是一个相当大的改造,然后将要求我们正在创建的库的任何用户都必须包装这些包含这些接口/派生类型的新类的每个实例化,才能使Google Mock工作。

在这一点上,我们正在对遗留代码进行操作,这些代码不能进行如此多的更改以合并Google Mock。我们也不能跳过测试这些新的类类型,什么是前进的最好方法?

EN

回答 1

Stack Overflow用户

发布于 2019-05-10 00:55:17

请看下面的代码。

代码语言:javascript
复制
#include <vector>
#include <string>
#include <iostream>
#include <memory>

using namespace std;

class Fruit
{
    public:
    Fruit(std::string colorIn) : color(colorIn) { }
    std::string color;
};

class Apple : public Fruit{
public:
    Apple() :  Fruit("Red") { appleType = "Honey Crisp"; }
    Apple(const Fruit& fruit) : Fruit(fruit.color) { appleType = "Honey Crisp"; }
std::string appleType;
};

class Banana : public Fruit{
public:
    Banana() :  Fruit("Yellow") { bananaType = "Dole"; }
    Banana(const Fruit& fruit) : Fruit(fruit.color) { bananaType = "Dole"; }
std::string bananaType;
};

void takeMyFruit(std::vector<shared_ptr<Fruit>>& fruits)
{
    if (!fruits.empty())
    {
        for (vector<shared_ptr<Fruit>>::const_iterator it = fruits.begin(); it != fruits.end(); ++it)
            std::cout << "Take my " << (*it)->color;
        // You shouldn't use the following two lines as you don't know what is in the vector.
        //        std::cout << "Take my " << fruits[0]->color << " " << std::dynamic_pointer_cast<Banana>(fruits[0])->bananaType << " banana." << std::endl;
        //        std::cout << "Take my " << fruits[1]->color << " " << std::dynamic_pointer_cast<Apple>(fruits[1])->appleType << " apple." << std::endl;
    }
    else
    {
        std::cout << "You gave me an empty bag?" << std::endl;
    }
}

int main()
{
    std::vector<std::shared_ptr<Fruit>> fruits;
    fruits.push_back(std::make_shared<Banana>());
    fruits.push_back(std::make_shared<Apple>());

    std::vector<std::shared_ptr<Fruit>> bananas = { std::make_shared<Banana>() };

    std::vector<std::shared_ptr<Fruit>> apples = { std::make_shared<Apple>() };

    takeMyFruit(fruits);    //Why can I do this?
    takeMyFruit(bananas); //OK now
    takeMyFruit(apples);  //OK now

    return 0;
}

如果您想要具有显示水果类型的功能,正确的方法是在fruit中添加一个虚拟函数

代码语言:javascript
复制
virtual string FruitDetailType() = 0;

并在派生类中实现它--比方说,apple类

代码语言:javascript
复制
virtual string FruitDetailType()
{
    return "Apple, Honey Crisp";
}
//or,
virtual string FruitDetailType()
{
    return appleType;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56063782

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档