Smart Pointers, this, and Constructors



Synoposis:将下面第一个代码块中的原始指针转换为智能指针,同时将其余功能保留为尽可能保持不变。


我在写一些代码,试图将指针传递到对象的构造函数中,目的是在构造函数返回时使该指针指向已构造的对象。

(注意:这源于一个更复杂的情况,但我已经将其简化为问题的本质。因此,如果你试图问为什么我这么做,你不一定会在这种情况下找到一个合理的答案,但这不是问题的重点。)

在努力实现智能指针之后,我恢复了原始指针,以确保我没有错过一个基本问题:

#include <iostream>
class Thing {
public: 
Thing(int data, Thing* thing_ptr) {
data_ = data;
*thing_ptr = *this;
}
void PrintData() {
std::cout << data_ << std::endl;
}
private:
int data_;
};
int main() {
Thing* thing_ptr;
Thing t(6, thing_ptr);
thing_ptr->PrintData();
return 0;
}

这一切顺利进行,但当我回到智能指针时,我似乎无法复制这种功能。基本问题是,我不知道如何在不做不必要的额外工作或行为不正确的情况下初始化智能指针。

我尝试的第一件事是:

#include <iostream>
#include <memory>
class Thing {
public: 
Thing(int data, std::unique_ptr<Thing>& thing_ptr) {
data_ = data;
thing_ptr = std::unique_ptr<Thing>(this);
}
void PrintData() {
std::cout << data_ << std::endl;
}
private:
int data_;
};
int main() {
std::unique_ptr<Thing> thing_ptr;
Thing th(6, thing_ptr);
thing_ptr->PrintData();
return 0;
}

我认为它失败了(运行时的核心转储),因为this实际上不是指向Thing的指针,而是指向一个未初始化的内存块的指针,该内存块的大小正好可以容纳Thing。事实上,我不能100%确定这里发生了什么,但由于unique_ptrshared_ptr都失败了,我决定首先初始化智能指针,然后将this分配给它的内容。

问题是,我无法用这个方法创建额外的对象(这反过来甚至需要添加一个额外的构造函数)。

Thing() : data_(0) {}
Thing(int data, std::unique_ptr<Thing>& thing_ptr) {
data_ = data;
if (!thing_ptr) {
thing_ptr = std::make_unique<Thing>();
}
*thing_ptr = *this;
}

在这里,我做了一个Thing::Thing(),只是把它分配到下一行,这肯定不是正确的方法。

有人能给我指个正确的方向吗?

智能指针最常见的用途是管理对象的生存期。但请注意,当您使用类类型声明变量时,如中

Thing th(6, thing_ptr);

对于该对象的生存期,您别无选择:如果在函数块中声明,它将在下一个封闭的}处结束;如果声明为类成员,则它将在包含对象的生命期结束时结束;或者如果声明为命名空间成员,则在程序结束时结束。使用默认deleter的std::unique_ptr<T>只能包含指向使用new创建的对象的指针,因为默认deleter尝试使用delete

如果你的指针总是指向具有像这样的普通声明的对象,而不是使用new创建的,那么你就只能自己确保指针只在它们指向的对象的生命周期内使用。在这种情况下,使用std::unique_ptr并没有多大好处;您可以继续使用原始指针,当其他东西(这里是C++语言本身)对生存期问题负责时,这仍然是表示指针的普通方式。

如果您希望指针的寿命超过创建对象的函数块,则不能使用普通的对象声明语法。一种可能的替代方案是要求所有对象创建都使用create函数,而不是直接声明对象:

class Thing {
public:
static std::unique_ptr<Thing> create(const Thing& src)
{ return { new Thing(src) }; }
static std::unique_ptr<Thing> create(int data)
{ return { new Thing(data) }; }
void PrintData() const;
private:
// All constructors private, to make sure a Thing can ONLY be
// created by a create() function - including the copy constructor.
Thing(const Thing&) = default;
explicit Thing(int data) : data_(data) {}
int data_;
};
int main() {
auto thing_ptr = Thing::create(6);
thing_ptr->PrintData();
}

最新更新