C++ 自动纠错设计

C++ 自动纠错设计,c++,C++,在我看来,类应该提供定义良好的抽象,在不了解类的情况下,不应该修改私有成员。但是,当我检查auto_ptr或任何其他智能指针时,违反了此规则。请参阅以下代码 class Foo{ public: Foo(){} }; int main(int argc, char* argv[]) { std::auto_ptr<Foo> fooPtr(new Foo); delete fooPtr.operator ->(); return 0; } 操作符重载-

在我看来,类应该提供定义良好的抽象,在不了解类的情况下,不应该修改私有成员。但是,当我检查auto_ptr或任何其他智能指针时,违反了此规则。请参阅以下代码

class Foo{
public:
   Foo(){}
};

int main(int argc, char* argv[])
{
   std::auto_ptr<Foo> fooPtr(new Foo);
   delete fooPtr.operator ->();
   return 0;
}
操作符重载->给出了底层指针,可以在不知道自动ptr的情况下对其进行修改。我不能认为这是一个糟糕的设计,因为智能指针是由C++极客设计的,但我想知道为什么他们会允许这样做。有没有办法编写一个没有这个问题的智能指针


欣赏你的想法。

< P>不,完全禁止C++中的这种不正确用法。 一般来说,任何库代码的用户都不应该在任何包装指针上调用delete,除非有明确的文档记录。在我看来,所有的现代C++代码都应该设计成让类的用户永远不承担手动释放她所获取的资源的责任。 旁注:std::auto_ptr不再是最好的选择。它在复制时的不良行为会导致严重的编码错误。通常更好的方法是使用std::tr1::scoped_ptr或std::tr1::shared_ptr或其变体


此外,在C++0x中,std::unique_ptr将在功能上取代std::auto_ptr,成为一个更安全的类。对UnjyJTR仿真的主题和最近的C++ 03实现进行了一些讨论。

< P>不,完全禁止C++中的这种不正确用法是不可能的。 一般来说,任何库代码的用户都不应该在任何包装指针上调用delete,除非有明确的文档记录。在我看来,所有的现代C++代码都应该设计成让类的用户永远不承担手动释放她所获取的资源的责任。 旁注:std::auto_ptr不再是最好的选择。它在复制时的不良行为会导致严重的编码错误。通常更好的方法是使用std::tr1::scoped_ptr或std::tr1::shared_ptr或其变体


此外,在C++0x中,std::unique_ptr将在功能上取代std::auto_ptr,成为一个更安全的类。可以找到关于这个主题的一些讨论和最近的C++03实现,用于独特的ptr仿真。

为了提供对底层对象的快速、方便、指针式访问,operator->不幸地泄漏了它的抽象。否则,智能指针必须手动包装所有允许公开的成员。这些都需要大量的配置工作,在那些实例化智能指针的部分,或者在C++中不存在的元编程级别。此外,正如皮斯塔所指出的,即使这个漏洞被堵住了,仍然有许多可能是非标准的方法来破坏C++的访问控制机制。

为了提供对底层对象的快速、方便、指针式访问,operator->不幸地不得不泄漏它的抽象。否则,智能指针必须手动包装所有允许公开的成员。这些都需要大量的配置工作,在那些实例化智能指针的部分,或者在C++中不存在的元编程级别。此外,正如皮斯塔所指出的,即使这个漏洞被堵住了,仍然有许多其他可能是非标准的方法来颠覆C++的访问控制机制

有没有办法编写一个没有这个问题的智能指针

这并不容易,一般来说是不行的,也就是说,你不可能对每一个普通的Foo类都这样做

要做到这一点,我能想到的唯一方法是更改Foo类的声明:将Foo析构函数设为private或将private delete操作符定义为Foo类的成员,并在Foo类的声明中指定std::auto_ptr是友元

有没有办法编写一个没有这个问题的智能指针

这并不容易,一般来说是不行的,也就是说,你不可能对每一个普通的Foo类都这样做


要做到这一点,我能想到的唯一方法是更改Foo类的声明:将Foo析构函数设为私有或将私有delete运算符定义为Foo类的成员,并在Foo类的声明中指定std::auto_ptr是朋友。

智能指针应有两个理想的属性:

可以检索原始指针,例如用于传递给传统库函数 无法检索原始指针以防止双重删除 显然,这些属性是矛盾的,不能同时实现!即使是Boost的shared_ptr等人也得到了,所以他们有这个问题。在实践中,第一个更重要,因此第二个必须放弃

顺便说一句,我不知道为什么当普通的旧get方法导致相同的问题时,您要使用稍微晦涩的操作符->:

std::auto_ptr<Foo> fooPtr(new Foo);
delete fooPtr.get();

智能指针应有两个理想的属性 d有:

可以检索原始指针,例如用于传递给传统库函数 无法检索原始指针以防止双重删除 显然,这些属性是矛盾的,不能同时实现!即使是Boost的shared_ptr等人也得到了,所以他们有这个问题。在实践中,第一个更重要,因此第二个必须放弃

顺便说一句,我不知道为什么当普通的旧get方法导致相同的问题时,您要使用稍微晦涩的操作符->:

std::auto_ptr<Foo> fooPtr(new Foo);
delete fooPtr.get();

我不认为这表明auto_ptr有封装问题。无论何时处理自有指针,人们都必须了解谁拥有什么。在auto_ptr的情况下,它拥有它持有的指针[1];这是auto炣ptr抽象的一部分。因此,以任何其他方式删除该指针都违反了auto_ptr提供的约定

<>我同意使用AutoMyPTR(2)是比较容易的,这不是很理想,但是在C++中,你永远不能避免谁拥有这个指针的根本问题,因为不管是好还是坏,C++都不为你管理内存。 [1] 引用cplusplus.com的话:auto_ptr对象具有拥有分配给它们的指针的特性:


[2] 例如,您可能会错误地认为它具有值语义,并将其用作向量模板参数:

我认为这并不表明auto_ptr存在封装问题。无论何时处理自有指针,人们都必须了解谁拥有什么。在auto_ptr的情况下,它拥有它持有的指针[1];这是auto_ptr抽象的一部分。因此,以任何其他方式删除该指针都违反了auto_ptr提供的约定

<>我同意使用AutoMyPTR(2)是比较容易的,这不是很理想,但是在C++中,你永远不能避免谁拥有这个指针的根本问题,因为不管是好还是坏,C++都不为你管理内存。 [1] 引用cplusplus.com的话:auto_ptr对象具有拥有分配给它们的指针的特性:


[2] 例如,您可能错误地认为它具有值语义,并将其用作向量模板参数:

我认为这个问题解决了一个非问题。智能指针用于管理指针的所有权,如果这样做会使指针不可访问,则无法达到其目的


也可以考虑这一点。任何容器类型都为您提供迭代器;如果它是这样一个迭代器,那么&*它是指向容器中某个项的指针;如果你说删除它,那么你就死定了。但是暴露其项目的地址并不是容器类型的缺陷。

我认为这个问题解决了一个非问题。智能指针用于管理指针的所有权,如果这样做会使指针不可访问,则无法达到其目的


也可以考虑这一点。任何容器类型都为您提供迭代器;如果它是这样一个迭代器,那么&*它是指向容器中某个项的指针;如果你说删除它,那么你就死定了。但是暴露其项目的地址并不是容器类型的缺陷。

是的,我认为这是唯一的方法。但是必须修改Foo,并修改所有将与auto_ptr一起使用的类。这太过分了。使用std::auto_ptr的人必须知道它是什么,以及如何使用它。有时,C++使坏事不可能发生;但有时它不能,而且它只会让事情变得简单,在这里,好的事情是记住删除对象,而坏的事情是删除它不止一次。在C++中,开发人员留下了所有的工具来破坏程序。最好的做法是记录代码的行为和正确使用。并遵循其他地方记录的最佳实践;是的,这是我唯一的想法。但是必须修改Foo,并修改所有将与auto_ptr一起使用的类。这太过分了。使用std::auto_ptr的人必须知道它是什么,以及如何使用它。有时,C++使坏事不可能发生;但有时它不能,而且它只会让事情变得简单,在这里,好的事情是记住删除对象,而坏的事情是删除它不止一次。在C++中,开发人员留下了所有的工具来破坏程序。最好的做法是记录代码的行为和正确使用。并遵循其他地方记录的最佳实践;谢谢我使用auto_ptr只是为了解释。我正在使用boost::shared\u ptr。RAII看起来很有趣谢谢。我使用auto_ptr只是为了解释。我正在使用boost::shared\u ptr。RAII看起来很有趣,这很有道理。谢谢,这很有道理。谢谢,我想不出有多少有用的类没有这样的东西。这很方便,但别傻了。删除[]向量[0];我想不出有多少有用的类没有这样的东西。这很方便,但别傻了。删除[]&
向量[0];Re用于传递给遗留库函数:不需要调用遗留库函数。任何不关心所有权的函数都不应将智能指针作为参数,而应将其作为原始指针或引用。除了所有权可能会妨碍许多智能指针不允许多个所有者之外,一个功能不应该将特定品牌的所有权管理强加给它的客户,除非它必须这么做。@MarcvanLeeuwen:好的观点,虽然我会将不关心所有权替换为不接受所有权-每个接口都应该关心,即明确其所有权语义。@MarcvanLeeuwen:实际上。。。你能想到一种情况吗?在这种情况下,一个非所有权接受函数最好是通过T*而不是通过T&接受一个参数?我不能,我认为后者更安全。我的意思是不关心函数所做的任何事情都与所有权无关,也不关心它想造成内存泄漏。但是,它也可以通过存储由可修改引用传递的指针所拥有的东西来放弃所有权,而不是获取所有权。对于另一个注释,最明显的情况是当指针可能为空时,T*是必需的。一个更为机会主义的论点可能是,您知道函数无论如何都需要初始化一个局部指针变量;您还可以传递该指针。Re用于传递给遗留库函数:不需要调用遗留库函数。任何不关心所有权的函数都不应将智能指针作为参数,而应将其作为原始指针或引用。除了所有权可能会妨碍许多智能指针不允许多个所有者之外,一个功能不应该将特定品牌的所有权管理强加给它的客户,除非它必须这么做。@MarcvanLeeuwen:好的观点,虽然我会将不关心所有权替换为不接受所有权-每个接口都应该关心,即明确其所有权语义。@MarcvanLeeuwen:实际上。。。你能想到一种情况吗?在这种情况下,一个非所有权接受函数最好是通过T*而不是通过T&接受一个参数?我不能,我认为后者更安全。我的意思是不关心函数所做的任何事情都与所有权无关,也不关心它想造成内存泄漏。但是,它也可以通过存储由可修改引用传递的指针所拥有的东西来放弃所有权,而不是获取所有权。对于另一个注释,最明显的情况是当指针可能为空时,T*是必需的。一个更为机会主义的论点可能是,您知道函数无论如何都需要初始化一个局部指针变量;你最好传递那个指针。