C ++中有哪些方法可以处理拥有另一个类实例所有权的类,其中该实例可能包含许多类,所有类都继承自公共类?
示例:
class Item { //the common ancestor, which is never used directly
public:
int size;
}
class ItemWidget: public Item { //possible class 1
public:
int height;
int width;
}
class ItemText: public Item { //possible class 2
std::string text;
}
假设还有一个类Container
,每个类都包含一个Item,而且任何人对Item都感兴趣的唯一时间就是它们从Container中取出它。我们还说只有在创建Container的同时才创建Items,以便将它们放在Container中。
有什么不同的方法来构建它?我们可以在Container中为包含的Item创建一个指针,然后将参数传递给Container的构造函数,以获取要调用new的类型的Item,这将把所有Items粘在堆中。有没有办法将Item与Container一起存储在堆栈中,这有什么好处吗?
如果Container和Items是不可变的,它是否有所不同,我们在创建时就知道它们的一切,并且永远不会改变它们?
答案 0 :(得分:3)
正确的解决方案如下:
class Container {
public:
/* ctor, accessors */
private:
std::unique_ptr<Item> item;
};
如果你有一个旧的编译器,你可以改用std::auto_ptr
。
智能指针确保容器对项目的严格所有权。 (你也可以把它作为一个普通的指针并卷起你自己的析构函数/赋值操作/复制ctor / move ctor / move assignment op / etc,但是unique_ptr
已经完成了所有这些,所以.. )
为什么你需要在这里使用指针,而不只是简单的构图?
因为如果你撰写,那么你必须知道将要编写的确切类。你不能引入多态。此外,所有Container
个对象的大小必须相同,Item
派生类的大小可能会有所不同。
如果你迫切需要撰写?
然后你需要尽可能多的Container
变体,因为每个这样的容器都有不同的大小,所以它是一个不同的类。你最好的镜头是:
struct IContainer {
virtual Item& getItem() = 0;
};
template<typename ItemType>
struct Container : IContainer {
virtual Item& getItem() {
return m_item;
}
private:
ItemType m_item;
};
答案 1 :(得分:2)
class AutoContainer
{
char buf[CRAZY_VALUE];
Base * p;
public:
template <typename T> AutoContainer(const T & x)
: p(::new (buf) T(x))
{
static_assert(std::is_base_of<Base, T>::value, "Invalid use of AutoContainer");
static_assert(sizeof(T) <= CRAZY_VAL, "Not enough memory for derived class.");
#ifdef __GNUC__
static_assert(__has_virtual_destructor(Base), "Base must have virtual destructor!");
#endif
}
~AutoContainer() { p->~Base(); }
Base & get() { return *p; }
const Base & get() const { return *p; }
};
容器本身不需要动态分配,您只能确保CRAZY_VALUE
足够大以容纳任何派生类。
答案 2 :(得分:1)
下面的示例代码编译并显示如何执行与您想要执行的操作类似的操作。这就是java中被称为接口的东西。看到你需要在类中至少有一些相似之处(在这种情况下是一个常见的函数名)。 virtual关键字意味着所有子类都需要实现此函数,并且无论何时调用该函数,实际上都会调用真实类的函数。
这些类是否为const不会对此造成伤害。但一般来说,你应尽可能保持正确。因为如果知道什么不会改变,编译器可以生成更好的代码。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class outputter {
public:
virtual void print() = 0;
};
class foo : public outputter {
public:
virtual void print() { std::cout << "foo\n"; }
};
class bar : public outputter {
public:
virtual void print() { std::cout << "bar\n"; }
};
int main(){
std::vector<outputter *> vec;
foo *f = new foo;
vec.push_back(f);
bar *b = new bar ;
vec.push_back(b);
for ( std::vector<outputter *>::iterator i =
vec.begin(); i != vec.end(); ++i )
{
(*i)->print();
}
return 0;
}
输出:
foo
bar
答案 3 :(得分:1)
在容器类中保存指针(最好是智能指针),并在需要复制时由派生类实现的Item类上调用纯虚拟clone()
成员函数。您可以通过完全通用的方式执行此操作:
class Item {
// ...
private:
virtual Item* clone() const = 0;
friend Container; // Or make clone() public.
};
template <class I>
class ItemCloneMixin : public Item {
private:
I* clone() const { return new I(static_cast<const I&>(*this); }
};
class ItemWidget : public ItemCloneMixin<ItemWidget> { /* ... */ };
class ItemText : public ItemCloneMixin<ItemText> { /* ... */ };
关于堆栈存储,您可以使用调用alloca()
的重载新内容,但这样做会造成危险。它只有在编译器内联你的特殊new运算符时才有效,你不能强制它执行(除了非可移植的编译器pragma)。我的建议是,这不值得加重;运行时多态性属于堆。