阅读本教程后提出了这个问题: http://www.cprogramming.com/tutorial/auto_ptr.html
在那里你可以找到以下声明:这种行为的一个微妙结果是auto _ ptrs在所有场景中都不能正常工作。例如,使用带有标准模板库的auto _ ptr对象可能会导致问题,因为STL中的某些函数可能会在容器(如向量容器类)中复制对象。一个例子是sort函数,它使容器中某些对象的副本被排序。因此,此副本可以轻松删除容器中的数据!
关于'auto_ptr'的大多数论文都告诉我们以下内容:
“切勿在STL容器中使用'auto_ptr'!他们经常在执行内部操作时复制其元素。例如,考虑sort
上的std::vector
”。
所以我的目标是编写代码示例来说明这一点,或证明这些示例在理论上只是真实而且在实践中很奇怪。
P.S。 @everybody_who_also_knows_that_auto_ptr_is_deprecated
我也知道这个。但是,您是否考虑过可能不允许使用新指针容器的技术原因(遗留代码或旧编译器)?而且这个问题是关于旧的和坏的(如果你愿意的话)auto_ptr
。
答案 0 :(得分:4)
我现在没有MSVC,但从g ++的错误判断,我猜这就是原因:
auto_ptr<T>
只有一个“复制构造函数”,它带有可变引用(§D.10.1.1[auto.ptr.cons] / 2-6):
auto_ptr(auto_ptr& a) throw();
template<class Y> auto_ptr(auto_ptr<Y>& a) throw();
但vector::push_back
将接受const引用(§23.3.6.1[vector.overview] / 2)。
void push_back(const T& x);
所以不可能通过push_back构造auto_ptr,因为没有构造函数接受const引用。
答案 1 :(得分:0)
结论是:我甚至无法编译这样的例子。为什么他们阻止我做一些我无法编译的事情?
答案 2 :(得分:0)
第1步 让我们直接解决这个问题:
#include <iostream>
#include <vector>
#include <algorithm>
template<> struct std::less<std::auto_ptr<int>>: public std::binary_function<std::auto_ptr<int>, std::auto_ptr<int>, bool> {
bool operator()(const std::auto_ptr<int>& _Left, const std::auto_ptr<int>& _Right) const
{ // apply operator< to operands
return *_Left < *_Right;
}
};
int wmain() {
using namespace std;
auto_ptr<int> apai(new int(1)), apai2(new int(2)), apai3(new int(3));
vector<auto_ptr<int>> vec;
vec.push_back(apai3);
vec.push_back(apai);
vec.push_back(apai2);
for ( vector<auto_ptr<int>>::const_iterator i(vec.cbegin()) ; i != vec.cend() ; ++i )
wcout << i->get() << L'\t';
vector<int> vec2;
vec2.push_back(3);
vec2.push_back(2);
vec2.push_back(5);
sort(vec2.begin(), vec2.end(), less<int>());
sort(vec.begin(), vec.end(), less<auto_ptr<int>>());
return 0;
}
在MSVCPP11上,错误文本如下: _ 错误1错误C2558:类'std :: auto _ptr&lt; Ty&gt;':没有可用的复制构造函数或复制构造函数被声明为'explicit'c:\ program files(x86)\ microsoft visual studio 11.0 \ vc \ include \ xmemory0 608
结论是:我甚至无法编译这样的例子。为什么他们阻止我做一些我无法编译的事情?他们的预防并非总是如此。
第2步
由于auto_ptr
设计,我们无法直接将vector
用作auto_ptr
元素类型。但是我们可以用下面给出的方式包装`auto_ptr'。
#include <iostream>
#include <vector>
#include <algorithm>
template<typename T> class auto_ptr_my: public std::auto_ptr<T> {
public:
explicit auto_ptr_my(T *ptr = 0) {
this->reset(ptr);
}
auto_ptr_my<T> &operator=(const auto_ptr_my<T> &right) {
*(static_cast<auto_ptr<T> *>(this)) = *(static_cast<auto_ptr<T> *>(const_cast<auto_ptr_my *>(&right)));
return *this;
}
auto_ptr_my(const auto_ptr_my<T>& right) {
*this = right;
}
};
template<> struct std::less<auto_ptr_my<int>>: public std::binary_function<auto_ptr_my<int>, auto_ptr_my<int>, bool> {
bool operator()(const auto_ptr_my<int>& _Left, const auto_ptr_my<int>& _Right) const
{ // apply operator< to operands
return *_Left < *_Right;
}
};
int wmain() {
using namespace std;
auto_ptr_my<int> apai(new int(1)), apai2(new int(2)), apai3(new int(3));
vector<auto_ptr_my<int>> vec;
vec.push_back(apai3);
vec.push_back(apai);
vec.push_back(apai2);
for ( vector<auto_ptr_my<int>>::const_iterator i(vec.cbegin()) ; i != vec.cend() ; ++i )
wcout << **i << L'\t';
sort(vec.begin(), vec.end(), less<auto_ptr_my<int>>());
for ( vector<auto_ptr_my<int>>::const_iterator i(vec.cbegin()) ; i != vec.cend() ; ++i )
wcout << **i << L'\t';
return 0;
}
此代码可以很好地显示 auto_ptr
可以与vector
和sort
一起使用,不会发生内存泄漏和崩溃。
第3步 正如KennyTM在下面发布的那样:
在return 0;
声明之前添加此代码:
std::vector<auto_ptr_my<int>> vec2 = vec;
for ( vector<auto_ptr_my<int>>::const_iterator i(vec2.cbegin()) ; i != vec2.cend() ; ++i )
wcout << **i << L'\t';
wcout << std::endl;
for ( vector<auto_ptr_my<int>>::const_iterator i(vec.cbegin()) ; i != vec.cend() ; ++i )
wcout << **i << L'\t';
wcout << std::endl;
... 让内存泄漏!
<强>结论强>
有时我们可以将auto_ptr
与容器一起使用而不会发生明显崩溃,有时则不然。无论如何,这是不好的做法。
但是不要忘记auto_ptr
的设计方式是你不能直接使用STL容器和算法:反对你必须编写一些包装代码。最后将auto_ptr
与STL容器一起使用是您自己的风险。例如,sort
的某些实现在处理vector
元素时不会导致崩溃,但其他实现将直接导致崩溃。
这个问题有学术目的。 感谢KennyTM提供的STEP 3崩溃示例!
答案 3 :(得分:0)
根据你所写的内容,你似乎已经知道了有关auto_ptr
s容器及其不安全原因的所有信息。
因此,我认为您对auto_ptr
s容器的兴趣纯粹是面向教学的。我理解你在尝试建立一个深思熟虑的反例时感到沮丧:实际上,大多数标准容器的实现者已经采用了解决办法来避免意外触发auto_ptr
s的破坏语义。
所以,这是一个我自己写的正是教学的例子:
class MyClass {
int a;
public:
MyClass (int i) : a(i) { }
int get() const { return a; }
};
int main() {
constexpr unsigned size = 10;
std::vector< std::auto_ptr<MyClass> > coap;
coap.resize(size);
for (unsigned u=0; u<size; u++)
coap[u] = std::auto_ptr<MyClass>( new MyClass( rand() % 50 ));
std::sort( coap.begin(), coap.end(),
[]( std::auto_ptr<MyClass> a,
std::auto_ptr<MyClass> b) { return a->get() < b->get(); });
}
使用g ++ 4.9.2进行编译将导致一个可以很好地进行segfault的可执行文件。
您可以使用类型推导更简洁地重写上面的示例:
std::sort( coap.begin(), coap.end(),
[]( auto a, auto b) { return a->get() < b->get(); });
请注意,问题不在std::sort
的具体实现中,这似乎是auto_ptr
- 安全。它是我传递给std::sort
的比较lambda函数,它故意通过值接受它的参数,因此每次执行比较时都会销毁容器中的对象。
如果你改变了lambda以便它通过引用接收它的参数,如下所示,大多数STL实现实际上会表现正常,即使你做的事情在概念上是错误的。
std::sort( coap.begin(), coap.end(),
[]( const std::auto_ptr<MyClass> & a,
const std::auto_ptr<MyClass> & b) { return a->get() < b->get(); });
祝你好运!
答案 4 :(得分:-2)
正确的答案是“永远不要使用auto_ptr” - 它已被弃用,并且从未成为标准的一部分,正是由于此处列出的原因。请改用std :: unique_ptr。