我正在阅读Top 10 dumb mistakes to avoid with C++11 smart pointer。 编号#5读取:
错误#5:没有将对象(原始指针)分配给shared_ptr as 一旦它被创建!
int main()
{
Aircraft* myAircraft = new Aircraft("F-16");
shared_ptr<aircraft> pAircraft(myAircraft);
...
shared_ptr<aircraft> p2(myAircraft);
// will do a double delete and possibly crash
}
,建议类似于:
使用
make_shared
或new
并立即构造指针 它
好的,毫无疑问是问题和建议。不过我对shared_ptr
的设计有疑问。这是一个非常容易犯的错误,shared_ptr
的整个“安全”设计可能会被非常容易的错误抛弃。
现在的问题是,是否可以通过shared_ptr
的替代设计轻松修复,其中来自原始指针的唯一构造函数将来自r值引用? < / p>
template<class T>
struct shared_ptr{
shared_ptr(T*&& t){...basically current implementation...}
shared_ptr(T* t) = delete; // this is to...
shared_ptr(T* const& t) = delete; // ... illustrate the point.
shared_ptr(T*& t) = delete;
...
};
这样shared_ptr
只能从new
或某些工厂函数的结果初始化。
这是否是对库中C ++语言的开发不足? 或如果这很可能是滥用,那么从原始指针(l值)引用中获取构造函数有什么意义呢?
这是历史性事故吗? (例如,在引入r值引用之前提出了shared_ptr等)向后兼容性?(当然有人可以说std::shared_ptr<type>(std::move(ptr));
更容易捕获,如果真的有必要,还可以解决这个问题。)
我错过了什么吗?
答案 0 :(得分:4)
指针很容易复制。即使您限制为r值参考,您也可以轻松地进行复制(例如,当您将指针作为函数参数传递时),这将使安全设置无效。此外,您会在模板中遇到问题,您可以轻松地将T* const
或T*&
作为类型,并且您会遇到类型不匹配。
因此,您建议在没有显着安全性的情况下创建更多限制,这可能是为什么它不符合标准的原因。
make_shared
的要点是雾化共享指针的构造。假设你有f(shared_ptr<int>(new int(5)), throw_some_exception())
。标准不保证参数调用的顺序。允许编译器创建一个新的int,运行throw_some_exception
然后构造shared_ptr,这意味着你可以泄漏int(如果throw_some_exception
实际抛出异常)。 make_shared
只是在其自身内部创建对象和共享指针,这不允许编译器更改顺序,因此它变得安全。
答案 1 :(得分:1)
在许多情况下,您可能无法拨打make_shared()
。例如,您的代码可能不负责分配和构造有问题的类。以下范例(私有构造函数+工厂函数)在某些C ++代码库中使用的原因有多种:
struct A {
private:
A();
};
A* A_factory();
在这种情况下,如果你想将A*
从A_factory()
粘到shared_ptr<>
,你必须使用带有原始指针的构造函数而不是{ {1}}。
脱离我的头脑,其他一些例子:
make_shared()
为您的类型获取对齐的内存,然后将其存储在posix_memalign()
中,并使用调用shared_ptr<>
的自定义删除工具(此用例很快就会消失)为语言添加对齐的分配!)。free()
创建的内存映射区域的指针添加到mmap()
中,并使用调用shared_ptr<>
的自定义删除工具(此用例会在我们获取时消失一个标准化的shmem设施,我希望在接下来的几个月里继续努力。)munmap()
中。答案 2 :(得分:1)
我对shared_ptr
的设计没有任何特别的了解,但我认为最可能的解释是所涉及的时间表使这不可能实现:
在C ++ 11中,shared_ptr
与rvalue-references同时引入。 shared_ptr
在boost
中已经有一个有效的参考实现,因此可以预期它会相对较快地添加到标准库中。
如果shared_ptr
的构造函数仅支持rvalue引用的构造,那么在编译器也实现对rvalue引用的支持之前,它将不可用。
那时,编译器和标准的开发是异步得多的,因此,可能要花费年,直到所有编译器都实现了支持(如果有的话)。 (2011年出口模板仍在人们的脑海中浮现)
此外,我认为标准委员会对标准化没有参考实现的API感到不自在,甚至在标准发布后也无法获得标准化。