std::auto_ptr 的最佳实践

3

我刚开始使用std::auto_ptr智能指针,现在想调用一个既接受auto_ptr又接受普通指针的函数。

auto_ptr<uint32> data_smart(new uint32[123])]);
uint32 data_fix[123];
uint32* data_dumb = new uint32[123];

processData(data_smart);
processData(data_fix);
processData(data_dumb);

如何在不过载的情况下做到最佳实践?使用带有uint32*参数的processData函数吗?我能否使用.get()将智能指针转换为uint32*?还是应该采取什么方式?

提前感谢您!


10
很抱歉告诉您,如果使用auto_ptr指向一个数组,就已经足够让您遭受惨重损失了。 - Edward Strange
2
auto_ptr是委员会设计的经典例子。唉。尝试使用boost::scoped_ptr和boost::shared_ptr代替。 - Mark Storer
2
不考虑数组使用1秒钟。std::auto_ptr<>用于指示所有权语义的转移,因此您使用它不正确。通常在方法API中指定它,以指示如果您将对象传递给该方法,则该方法将拥有该对象。在这种情况下,您应该使用data_fix(最好通过引用传递给processesData())。 - Martin York
8个回答

5

1.

auto_ptr<uint32> data_smart(new uint32[123])]);

不要这样做。 auto_ptr 仅适用于标量(它调用delete而不是delete[])。

2. auto_ptr 拥有它所指向的对象,因此除非你想将所有权传递给该函数(在你的代码中不需要),否则该函数应接受一个普通指针。因此,你应该更改调用为:

processData(data_smart.get());

为了明确地表达data_smart继续拥有该对象。

1

最佳实践是不要使用auto_ptr。它将在C++0x中被弃用,并由std::unique_ptr替换(参考:C++0x草案标准,附录D,第10段)。与此同时,替代方案包括std::tr1::shared_ptrboost::scoped_ptr

但是你的例子是一个数组,而那些指针类型不适用于数组。你可以使用boost::shared_array来解决这个问题。

然而,标准本身没有数组智能指针。这可能是因为他们认为你应该使用std::vector代替(或者在编译时知道大小的固定大小数组std::array)。鉴于此,你可以这样做:

std::vector<uint32> dataVector;
data.reserve(123);

// or, if the size is always 123:
std::tr1::array<uint32, 123> dataArray;

现在,您可以调用接受常规普通的uint32*的函数,因为vectorstd::tr1::array都有方法,可以将数据作为指向C风格数组的指针访问:
processData(&dataVector[0]);
processData(dataArray.data());

如果你要这样做,我强烈建议添加边界检查。将数组的大小作为第二个参数传递给processData

processData(&dataVector[0], dataVector.size());

如果你可以完全放弃C风格的指针/数组,更好的方法可能是通过引用传递:

void processData(std::vector<uint32>& data) {
    // process the data
}

// call it like this:
processData(dataVector);

但这仅适用于vector,而不适用于std::tr1::array或任何其他容器。因此,更进一步,您可以使用接受迭代器的模板:

template <class AnIterator>
void processData(AnIterator begin, AnIterator end) {
    for (AnIterator it = begin; it != end; ++it) {
        // process each item
    }
}

// call it like this
processData(dataVector.begin(), dataVector,end());

// or like this
processData(dataArray.begin(), dataArray.end());

// or even like this (assume c_data is a C-style array):
processData(c_data, c_data + number_of_items_in_c_data);

最后一个有效是因为指向C风格数组的指针可以用作迭代器。


1

编辑:Noah Roberts在您的问题上的评论是更大的问题,但即使示例代码有误,这也回答了所提出的问题....


如果你想在不过载的情况下完成它,唯一可行的选择是使该方法接受一个愚蠢指针参数。
能否将智能指针转换为uint32*?
不可以。请使用std::auto_ptr::get()。

好的,这可以通过使用模板来完成,这在技术上并不是“重载”。但是,当然,他们仍然面临着尝试使用错误类型的智能指针的严重问题。 - Edward Strange

1
首先,不要使用指向数组的指针来初始化auto_ptr。这是不被支持的,并且会导致内存泄漏。std::auto_ptr只处理单个对象。
如果你仍然想要使用std::auto_ptr,但只针对单个对象,你需要记住std::auto_ptr在复制构造函数中转移所有权。这意味着如果你按值传递data_smart,并调用processData函数后,你的本地auto_ptr(data_smart)将不再持有任何内存。
最后,你可能想使用boost::scoped_array或boost::shared_array。

2
"你最终会遇到未定义行为" - John Dibling
我同意您关于标准术语的看法,但未定义行为是指任何超出标准范围的内容。使用“内存泄漏”作为替代品并不是表达未定义行为最正式的方式,但在这种情况下,我认为这是常识。 - LavaScornedOven
对不起,Vedran,但你错了。你会混淆内存管理器。简单事实是,在应该调用 'delete[]' 的时候调用 'delete',可能并经常会像在你新建的东西上调用 free() 一样糟糕。混淆的内存管理器导致垃圾正是“未定义”的定义,例如,“当我尝试在我刚刚调用的看似不相关的函数中创建向量时,为什么我的代码在运行第二个星期二的发布版本时崩溃?” - Edward Strange
我明白了,谢谢你纠正我。我必须承认我从来没有遇到过这样的问题(因为我不使用auto_ptr来处理数组 :)),但我想现在是时候拿出一些Tanenbaum的书来看看了。 - LavaScornedOven

0
在您的情况下,使用向量是最安全的选择:
std::vector<uint32> data(123);

processData的签名应该是这样的:

void processData(const std::vector<uint32> & data);

然而,这个更常用:

void processData(uint32 * bytes, int length);

在这两种情况下,您都可以使用向量:
// 1
processData(data);

// 2
processData(data.data(), data.size());

好的,为了快速解决问题,我将使用自己的包装类来处理我的数据,该类是静态创建的,并构造/析构数据缓冲区。对于应用程序中不那么高速的部分,我将使用向量。 - vls

0

0

有趣的是,data_smart变量是其中最愚蠢的变量。也就是说,当该作用域结束时,auto_ptr析构函数将对其指针调用delete而不是delete[],导致UB(这比来自data_dumb的可能内存泄漏更糟)。

因此,重点是不要使用auto_ptr处理数组,应该使用vector。

进入真正的问题。首先,如果可能的话,请使用引用参数而不是指针参数。如果不可能,请使用裸指针,auto_ptr::get()可访问底层指针。


0

忽略已经被强调的,不要在数组上使用auto_ptr。

没有重载,这个问题的最佳实践是什么?

看起来你的方法不会拥有所有权,所以剩下的问题是它是否会被更改?

将processData函数与uint32*参数一起使用?

processData( uint32* ) 这是一个选项,但可能不是最好的。
processData( uint32[123] ) 如果你不编辑(123开始推动一些复制)。
processData( uint32 &[123] ) 通过引用并根据需要应用const。

我可以将智能指针转换为uint32*,然后使用.get()吗?

您可以使用get()获取智能指针的指针内容,它已经“类型化”,因此无需进行转换。

另外: 在这样一个原始级别上的数据和操作应该在一个类中,您可能甚至不需要将应该是成员变量的内容传递到成员函数中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接