想象一下java或C#中的以下类:
class A
{
B b;
//some other stuff
public A(B b) {this.b = b;}
}
class B
{
A createA() {return new A(this); }
}
然后我们会使用它,例如
A complicatedCreateA()
{
B = new B();
return b.createA();
}
并且虚拟机/ CLR将确保我们不会泄漏内存。
如何在C ++中实现类似的模式,以便我不泄漏内存并引用已清理的资源?
编辑: 为了使它更清楚,我特别担心如果我不止一次调用createA()会发生什么,并且当不同的对象A具有不同的生命周期时,例如:
A anotherMethod()
{
B = new B();
A a = b.createA();
//use a locally, or use with yet another object C, etc.
return b.createA();
}
我基本了解智能指针在C ++中的工作原理。但是,即使我做了类似的事情:
boost::shared_ptr<B> b(new B());
然后我无法从B内部访问这个智能指针,所以我不能将它传递给A.否则A可以确保相应的对象B被删除不是太晚而不是太早?
答案 0 :(得分:3)
只在堆栈上分配它们(最好)或(在你不能的时候)使用smart pointers,这样就不需要手动内存管理,并且在正确使用时是异常安全的。
class B;
class A
{
public:
std::unique_ptr<B> b;
A(B b) : b(make_unique<B>(b)) { }
};
class B
{
public:
std::unique_ptr<A> createA() { return make_unique<A>(*this); }
};
然后
std::unique_ptr<A> complicatedCreateA()
{
B b; // or std::unique_ptr<B> bptr(make_unique<B>()); but here that's useless more work
return b.createA();
}
您无需在delete
或成员createA
的返回值上调用A::b
,因为只要unique_ptr
超出范围,他们就会将清理他们拥有的A*
和B*
。
请注意,您致电make_unique<T>
制作unique_ptr
。这比普通new
更具例外安全性,但您也可以使用: b(new B(b))
和return std::unique_ptr<A>(new A(*this));
。
当你需要有多个指向同一个对象的指针时,还有引用计数shared_ptr
。
答案 1 :(得分:3)
确切的等价物有点复杂:
class A
{
std::shared_ptr<B> b;
//some other stuff
public:
A(std::shared_ptr<B> const & b) : b(b) {}
};
class B : public std::enable_shared_from_this<B>
{
public:
A createA() {return A(shared_from_this());}
};
A complicatedCreateA()
{
std::shared_ptr<B> b = std::make_shared<B>();
return b->createA();
}
或者,您可以通过使shared_from_this
成为非成员(或静态成员,如果它需要访问createA
私有权限)来避免B
malarkey,采用共享指针参数:
A createA(std::shared_ptr<B> const & b) {return A(b);}
A complicatedCreateA()
{
std::shared_ptr<B> b = std::make_shared<B>();
return createA(b);
}
答案 2 :(得分:1)
说“使用智能指针”不是一个简单的例子。 B似乎是A的工厂.A有一个指向创建它的工厂对象的指针(可能可以从中创建更多的A类对象)。
如果您希望B被引用计数,则必须对其进行“侵入式”引用计数。这通常是通过某种addRef()/ release()机制来完成的,当调用release()时,它最后一次执行删除这个
我也可以假设A在某种程度上是多态的,这样你就可以通过它的抽象工厂获得一个对象来创建更多自己类型的对象(不一定是克隆)。
问题是,第一个B来自哪个创造了第一个A?你有很多这些B对象吗?你真的想在删除最后一个A的那一刻摆脱它,或者它的生命周期在其他地方管理。
关于A,你的B会创建一个新的常规new,然后你会把它放到shared_ptr或其他任何你自己。
请注意,您可以在此处使用引用,因此:
class A
{
private:
friend class B;
explicit A( B const& b );
B (const) & b_;
A(const A&); // not implemented
public:
A* createNew() const;
~A();
};
A::A( B const& b )
: b_( b )
{
b_.addRef(); // possibly, if reference counted
}
A::~A()
{
b_.release();
}
A* A::createNew() const
{
return b_.createA();
}
代表B
A* B::createNew() const
{
return new A(*this); // addRef() will be invoked
}
void B::addRef() const
{
atomically_increment( &my_refCount );
}
void B::release() const
{
if( !atomically_decrement( &my_refCount ) )
delete this;
}
其中B有一个可变的my_refCount类型,适合您系统的原子递增/递减调用。
请注意,您也可以将boost shared_ptr用于B,但需要从boost::enable_shared_from_this<B>
派生B.这也是“侵入性”,但会为你做很多工作。您的A现在需要存储boost::shared_ptr<B>
而B :: createA()将调用shared_from_this()
。
要这样做,您需要创建第一个B并将其放入shared_ptr以创建第一个A.
答案 3 :(得分:0)
如果您有权访问boost,则可以使用智能指针。根据您想要实现的目标,您可以使用boost::shared_ptr
(引用计数一个)或boost::scoped_ptr
。
查看:http://www.boost.org/doc/libs/1_48_0/libs/smart_ptr/smart_ptr.htm
答案 4 :(得分:0)
为了说得更清楚,我特别担心如果我不止一次调用createA()会发生什么,以及当不同的对象A具有不同的生命周期时,例如:
A anotherMethod() {
B b = new B();
A a = b.createA(); //use a locally, or use with yet another object C, etc.
return b.createA();
}
根本不需要使用new B();
创建对象,您只需在堆栈上创建它们,它们的生命周期就会自动处理。
就这样做:
A anotherMethod() {
B b;
A a = b.createA(); //use a locally, or use with yet another object C, etc.
return b.createA();
}
与其他一些语言不同,这不是对堆对象的引用,而是对复制的值的引用。副本将在其各自的生命周期结束时销毁。自动!