c++ - 标准容器中的抽象类

标签 c++ c++11 virtual-functions

我经常在编程时使用多态性,因为它自然地为我需要的对象建模。另一方面,我经常使用标准容器来存储这些对象,并且我倾向于避免使用指针,因为这要么要求我释放对象而不是将它们从堆栈中弹出,要么要求我确定对象将保留在我使用指针时的堆栈。当然,有各种各样的指针容器对象可以为您完成这项任务,但根据我的经验,它们也不理想甚至令人讨厌。那是;如果存在这样一个简单的解决方案,它应该是用 C++ 语言编写的,对吧? ;)

让我们举一个经典的例子:

#include <iostream>
#include <vector>

struct foo {};
struct goo : public foo {};
struct moo : public foo {};

int main() {
    std::vector<foo> foos;
    foos.push_back(moo());
    foos.push_back(goo());
    foos.push_back(goo());
    foos.push_back(moo());

    return 0;
}

参见:http://ideone.com/aEVoSi .这工作正常,如果对象具有不同的 sizeof,编译器可能会应用切片。然而,由于 C++ 不像 Java 那样知道 instanceof 这一事实,而且据我所知,不存在足够的替代方案,因此在从 vector 作为 foo 获取继承类的属性后,无法访问它们。

因此可以使用虚函数,但是这不允许分配 foo,因此不允许在 vector 中使用它们。参见 Why can't we declare a std::vector<AbstractClass>? .

例如,我可能希望能够打印两个子类,简单的功能,对吧?

#include <iostream>
#include <vector>

struct foo {
        virtual void print() =0;
        virtual ~foo() {}
};

struct goo : public foo {
    int a;
    void print() { std::cout << "goo"; }
};

struct moo : public foo {
    int a,b;
    void print() { std::cout << "moo"; }
};

int main() {
    std::vector<foo> foos;
    foos.push_back(moo());
    foos.push_back(goo());
    foos.push_back(goo());
    foos.push_back(moo());

    for(foo& f : foos) {
        f.print();
    }
    return 0;
}

来源:http://ideone.com/I4rYn9

这是一个简单的添加,作为一名设计师,我永远不会想到要有远见的这种行为。我已经对 c++ 能够对我的对象进行切片并因此将不同大小的对象存储在一个 vector 中这一事实感到非常兴奋。不幸的是,当基类是抽象的时,它不能再这样做了,如下所述:Why can't we declare a std::vector<AbstractClass>?

一般好的解决方案似乎是使用指针。但是这 (1) 迫使我进行内存管理,并且 (2) 我需要更改接口(interface)并重新编码很多东西。例如,假设我首先有一些类接口(interface)返回一个 std::vector,现在它返回一个 std::vector,所以我需要检查并更改 foo 的所有调用;如果我正在编写一个库,这很烦人,甚至是不可能的。

所以基本上,恕我直言,这是一个小的功能添加大的代码后果

我的问题是 w.r.t.编码标准。我怎样才能防止这些烦恼的发生?我是否应该始终使用指针并进行所有内存管理?我是否应该始终假设一个类可能会在此过程中变得抽象?

编辑,回答:根据 40two 的回答,我制作了这个片段:

#include <iostream>
#include <vector>
#include <memory>

struct foo {
    virtual void print() =0;
};

struct goo : public foo {
    int a;
    void print() { std::cout << "goo"; }
};

struct moo : public foo {
    int a,b;
    void print() { std::cout << "moo"; }
};
typedef std::unique_ptr<foo> foo_ptr;
int main() {
    std::vector<std::unique_ptr<foo> > foos;
    foos.push_back(foo_ptr(new moo));
    foos.push_back(foo_ptr(new goo));
    foos.push_back(foo_ptr(new goo));
    foos.push_back(foo_ptr(new moo));

    for(auto it = foos.begin(); it!=foos.end(); ++it) {
        it->get()->print();
    }
    return 0;
}

来源:http://ideone.com/ym4SY2

最佳答案

如果您的编译器支持 C++11 功能,一个解决方案是使用 std::vector<std::shared_ptr<foo>>std::vector<std::unique_ptr<foo>>而不是像下面的例子那样的原始指针:

#include <iostream>
#include <memory>
#include <vector>

struct foo {
    virtual void print() = 0;
};

struct goo : public foo {
    int a;
    void print() { std::cout << "goo"; }
};

struct moo : public foo {
    int a,b;
    void print() { std::cout << "moo"; }
};

auto main() -> int {
    std::vector<std::shared_ptr<foo>> v{std::make_shared<goo>(), std::make_shared<moo>()};
    for(auto i : v) { 
        i->print();
        std::cout << std::endl;
    }
    return 0;
}

或使用 std::vector<std::unique_ptr<foo>> :

auto main() -> int {
    std::vector<std::unique_ptr<foo>> v;
    v.push_back(std::move(std::unique_ptr<goo>(new goo)));
    v.push_back(std::move(std::unique_ptr<moo>(new moo)));
    for(auto it(v.begin()), ite(v.end()); it != ite; ++it) { 
        (*it)->print();
        std::cout << std::endl;
    }
    return 0;
}

因此,您不必担心内存重新分配。

关于c++ - 标准容器中的抽象类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24125669/

相关文章:

c++ - 点云库 - 如何将单个 RGB 值分配给整个点云?

c++ - 使用 std::bind 在 lambda 中移动捕获 std::unique_ptr 时遇到问题

c++ - 具有两个可以采用多种不同类型的操作数的虚函数

C++ 类函数别名

c++ - 窗口类注册失败

c++ - 确定选择了哪个重载

c++ - 覆盖非虚函数和虚函数有什么区别?

C++ 如何实现模板化转换二元运算符重载

c++ - unique_ptr push_back 和 std::list

c++ - 派生纯虚函数的实现