【发布时间】:2014-07-30 07:28:40
【问题描述】:
当我编程时,我经常使用多态性,因为它自然地对我需要的对象进行建模。另一方面,我经常使用标准容器来存储这些对象,并且我倾向于避免使用指针,因为这要么需要我释放对象而不是将它们从堆栈中弹出,要么需要我确定这些对象会继续存在我使用指针时的堆栈。当然,有各种各样的指针容器对象可以为您完成这项任务,但根据我的经验,它们也不理想,甚至令人讨厌。那是;如果存在这样一个简单的解决方案,它应该是 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,并且据我所知,不存在适当的替代方案,因此在从向量中将继承类的属性作为 foo 获取后,无法访问它们。
因此可以使用虚函数,但是这不允许分配 foo,因此不允许在向量中使用它们。见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;
}
这是一个简单的补充,作为一名设计师,我永远不会想到要预见到这种行为。 C++ 能够对我的对象进行切片并因此将不同大小的对象存储在一个向量中,这让我感到非常激动。不幸的是,当基类是抽象的时,它不能再这样做了,如下所述:Why can't we declare a std::vector<AbstractClass>?
一般的好解决方案似乎是使用指针。但这(1)迫使我进行内存管理,(2)我需要更改接口并重新编码很多东西。例如,假设我首先有一些类接口返回一个 std::vector
所以基本上,恕我直言,这是一个小功能添加与大代码后果。
我的问题是 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;
}
【问题讨论】:
-
你的第二个例子产生了未定义的行为,因为抽象类没有虚拟析构函数
-
如果可能,您能否用最少的额外代码对示例进行分叉修改,以便定义行为?因为我认为这不是我的代码的重点 :) 我看到你这样做了,谢谢!
-
“我已经为 C++ 能够对我的对象进行切片并因此将不同大小的对象存储在一个向量中这一事实而激动不已。”您似乎完全误解了切片是什么。切片是转换的(坏)名称。它占用了您的
moo,并从中生成了foo不再是moo。 “切片”几乎总是一件坏的事情。向量不存储不同大小的对象,它只存储foo个对象,它们都是相同大小的。 -
@Casey:它甚至无法编译,因为
vector无法使用抽象值类型进行实例化。链接代码显示“错误:无法分配抽象类型‘foo’的对象” -
“切片”的工作方式如下:
std::vector<short> v; v.push_back(1000000000);。发生什么了?该向量包含一个短整数,而不是一个整数。
标签: c++ c++11 virtual-functions