std :: shared_ptr或std :: unique_ptr赋值运算符重载

时间:2012-07-01 15:38:22

标签: c++ shared-ptr smart-pointers unique-ptr

我没有看到为什么这些没有为它们被模板化的类型的普通旧指针赋值操作符重载的原因。如果使智能指针的接口尽可能接近普通旧指针,那么为什么它们不会像这样对赋值运算符进行重载?

inline std::shared_ptr<type> &operator=( const type * pointer)
{
    reset(a);
}

这样你可以像使用普通指针一样使用它们,如下所示:

std::shared_ptr<int> test = new int;

这根本不是一个问题,只是想知道为什么他们遇到了让一些运营商超载的麻烦。

还想知道是否有办法重载全局赋值运算符来执行此操作,或者是否有任何原因我不应该这样做。

编辑:在此处为Nawaz添加关于代码格式的回答。我刚刚编写了这个测试程序,看看你说的是对的:

template<class T>
class peh
{
public:
    peh() {meh = 3;}
    const peh<T> & operator=(const int * peh)
    {
    }
};

void f( peh<int> teh)
{

}

int main()
{
    int * meh = new int;

    f(meh);

    system("PAUSE");
    return 0;
}

这里错误地说没有从peh<int>int *的可用转换。那么为什么std::shared_ptr<int>int *可以接受?

2 个答案:

答案 0 :(得分:5)

  

还想知道是否有办法重载全局赋值运算符来执行此操作,或者是否有任何原因我不应该这样做。

没有。赋值运算符重载必须是成员函数。

顺便说一下,如果你想要以下功能,那么你不应该谈论赋值运算符,你应该问:为什么将原始指针作为参数的构造函数设为explicit?为什么不隐含?

//this code requires an implicit constructor, not assignment!
std::shared_ptr<int> test = new int;  //illegal 

这是非法的,但假设有一段时间允许这样做,那么你就可以调用以下函数传递一个原始指针作为参数:这样的特征将是危险,因为其余的答案解释了(阅读评论):

void f(std::shared_ptr<int> test)
{
     //code

} //test will be destructed here  (when it goes out of scope)
  //if test.use_count() == 1, then the pointer which it manages 
  //will be destructed as well. (NOTE THIS POINT)

现在看危险部分:

int *ptr = new int;

f(ptr); 
//note that calling f is allowed if it is allowed:
//std::shared_ptr<int> test = new int;
//it is as if ptr is assigned to the parameter:
//std::shared_ptr<int> test = ptr;

//Question : now what happened in f()?
//Answer : inside f(), test (the shared_ptr) will infer that no one else
//refers to the pointer it contains, because test.use_count() == 1
//test is obviously wrong in this case, because it cannot prove that!

//DANGER
*ptr = 10; //undefined behavior, because ptr is deleted by the shared_ptr

请阅读评论。它解释了上面代码片段的每个部分。

答案 1 :(得分:2)

您显示的operator=实际上不会启用您想要的语法。 shared_ptr<int> p = new int;将使用来自T *和shared_ptr的复制构造函数的shared_ptr构造函数。 shared_ptr具有这两个,但是你的语法不起作用,因为T *中的构造函数是explicit

原因是因为如果构造std::shared_ptr<int> test = new int;可以隐式完成,那就意味着shared_ptr可以取得指针的所有权而没有任何人明确要求它。 Nawaz显示了一个真正容易出错的原因;你必须要非常小心,你所使用的指针在你不知情的情况下不会被某个地方的shared_ptr突然采用,然后从你身下被摧毁。

这是一个显示这种危险的隐式结构的例子:

#include <iostream>

template<typename T>
struct owning_pointer {
    T *t;
    owning_pointer(T *t) : t{t} {}
    ~owning_pointer() {
        std::cout << t << " deleted\n";
        delete t;
    }
};

void foo(owning_pointer<int> thief) {}

int main() {
    int *i = new int;
    std::cout << i << " allocated\n";
    foo(i);
}

输出类似于:

0x10d400880 allocated
0x10d400880 deleted

查看将explicit添加到owning_ptr的构造函数时出现的错误。我明白了:

main.cpp:18:5: error: no matching function for call to 'foo'
    foo(i);
    ^~~
main.cpp:13:6: note: candidate function not viable: no known conversion from 'int *' to 'owning_pointer<int>' for 1st argument;
void foo(owning_pointer<int> thief) {}
     ^

此外,不必从T *中允许隐式构造,因为已经有一些非常简单的分配方法,而没有相同的错误可能性:

std::shared_ptr<int> test(new int); // one extra character isn't a hardship. I typically prefer () construction anyway.
std::shared_ptr<int> test{new int}; // although I might start preferring {} construction in C++11
auto test = std::make_shared<int>(); // this is slightly different in that the allocated int is zero-initialized

如果您正在初始化成员shared_ptr,那么您可以在初始化列表中初始化它,而不是在构造函数体中使用赋值或reset()

struct foo {
    std::shared_ptr<int> m_meh;
    foo()
      : m_meh(new int)
    {
        // no need for m_meh.reset(new int) here
    }
};

operator=启用的是:

shared_ptr<int> s;
s = new int;

这似乎不像T *中隐式构造shared_ptr那样容易出错,但我看不出它真的有任何价值。