我有一个类A
和一个结构B
(它是一个普通的旧数据结构)。这两个模型以A
在某种意义上表示整个系统的状态的方式对系统进行建模,并且由多个B
和其他状态组成。这意味着B
在没有A
的情况下可能毫无价值。我想制作一个API,您可以在其中添加" B
到A
,然后直接修改B
s的状态(通过保留类似对它们的引用)。
但我不确定的是如何根据所有权来设计这个。我有几个选择。由于我使用的是C ++,这很复杂。如果我使用Java,我可能会设计这样的代码:
class A {
private List<B> bs = new ArrayList<>();
public void add(B b) {
bs.add(b);
}
}
API的用户自己创建B
,或者像这样:
class A {
private List<B> bs = new ArrayList<>();
public B create(int data) {
B b = new B(data);
bs.add(b);
return b;
}
}
A
系统自己创建对象。这可能是更好的方法,因为B
没有A
就没有了。我甚至可能以某种方式使B
构造函数只能被A
访问,但我不确定如何。
但请记住,那是在Java中。这个系统用C ++编程,它的工作方式不同。
我想第一个例子看起来很相似:
class A {
public:
void add(B& b)
{
bs.push_back(&b);
}
private:
std::vector<B*> bs;
};
但由于我更喜欢第二种选择,我试图翻译它:
class A {
public:
B& create(int data)
{
bs.emplace_back(data);
return bs.back();
}
private:
std::vector<B> bs;
};
但在这种情况下,我将返回对B
的引用。调整bs
时,它可能会在内存中移动,从而使返回的引用无效。你会如何解决这个问题?或者你会尝试以其他方式设计这个?
答案 0 :(得分:0)
没有理由不能在C ++中使用任何一种数据模型,它只是不同的语法。
在第一种情况下,add
的调用者必须实例化B
的实例。然后它将B传递给A
的实例,然后b
将一个引用(或指针,用C ++术语)保存到#include <memory>
#include <vector>
class B {
};
class A {
public:
void add(std::shared_ptr<B> b) {
bs.push_back(b);
}
private:
std::vector<std::shared_ptr<B> > bs;
};
int main(int argc, const char *argv[]) {
A a;
std::shared_ptr<B> b(new B);
a.add(b);
}
:
shared_ptr
在这个实现中,我们持有每个B实例的共享指针。共享指针使用类似于Java处理内存管理的引用计数器。只要某人拥有对B的特定实例的引用(通过shared_ptr
),该特定实例将保留在内存中。 shared_ptr
与Java的不同之处在于 引用计数decriments为0时,实例将被删除。 Java等待将来垃圾收集的某些方面。这种模式在C ++中称为RAII(资源获取是初始化)。
您的第二个模型可以类似地处理。您可以直接在向量或原始指针中使用shared_ptr
。由于我已经在上面展示了class B {};
class A {
public:
~A() {
for (std::vector<B*>::iterator it = bs.begin(); it != bs.end(); ++it) {
delete *it;
}
bs.clear();
}
B* create(int data) {
B *b = new B(data);
bs.push_back(b);
return b;
}
private:
// block copy construction since we don't manually copy the contents of our
// vector
A(const A&) {}
void operator=(const A&) {}
std::vector<B*> bs;
};
,我将在这里显示原始指针:
B
在这个设计中,由于我们使用了原始指针,我们必须自己管理内存。这引入了对析构函数的需求,该析构函数在A
的实例被销毁时释放存储在向量中的所有B
实例。我们现在还返回指向我们在create
中创建的每个新B
实例的指针。这个内存模型比第一种方法更模糊,因为我们已经有了原始指针,因此需要采用一种策略,这种策略只能通过纪律强制执行,关于谁拥有并负责每个内存管理。 B
的实例。针对此API编码的开发人员可以删除返回给他的B实例,这会在您下次尝试访问shared_ptr
的实例时导致段错误或未定义的行为。
我的推荐?使用{{1}}除非这需要快速性能并且每次操作都很重要。
修改强>
按照Yakk的建议隐藏复制构造函数和赋值运算符。