开发者

Best practice for std::auto_ptr

开发者 https://www.devze.com 2023-01-30 13:59 出处:网络
I\'m just getting used to smart pointers using std::auto_ptr. Assume that I want to call a function with both auto_ptr and normal Pointers.

I'm just getting used to smart pointers using std::auto_ptr.

Assume that I want to call a function with both auto_ptr and normal Pointers.

开发者_如何学运维
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);

What is the best practice for this without overloading? Having the processData function with a uint32* argument? Can I cast the smart pointer to uint32* for this case with .get()? Or what is the way I should do it? Thanks in advance!


1.

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

Don't do that. auto_ptr works with scalars only (it calls delete rather than delete[]).

2. auto_ptr owns the object it points to, so unless you want to pass the ownership to that function (in your code you don't), the function should accept a normal pointer. So you should change the call to:

processData(data_smart.get());

in order to explicitly express that data_smart continues to own the object.


EDIT: Noah Roberts' comment on your question is the bigger issue here, but this answers the question asked even if the example code is wrong....


... without overloading ...

If you want to do it without overloading, the only option that's going to work for all of these is to make the method take a dumb pointer parameter.

Can I cast the smart pointer to uint32* for this case?

No. Use std::auto_ptr<t>::get().


First of all, you don't initialize auto_ptr with a pointer to array. It's not supported, and you'll end up with memory leaks. std::auto_ptr handles only single objects.

If you still want to use std::auto_ptr, but for single objects only, you need to remember that std::auto_ptr transfers ownership in copy constructor. That means that your local auto_ptr (data_smart) won't hold any memory after you call processData if you pass data_smart by value.

In the end, you probably want to use boost::scoped_array or boost::shared_array.


Best practice is to not use auto_ptr. It will be deprecated in C++0x and replaced by std::unique_ptr (Reference: C++0x Draft Standard, Appendix D, Paragraph 10). In the meantime, alternatives include std::tr1::shared_ptr and boost::scoped_ptr.

But your example is an array, and those pointer types are not for arrays. You can use boost::shared_array for that.

However the Standard itself does not have array smart pointers. That’s probably because they believe you should be using std::vector instead (or std::array for fixed size arrays when you know the size at compile time). Given that, you could do the following:

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

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

Now, you can call your function that accepts a regular plain-old uint32* because both vectors and std::tr1::arrays have methods to give you access to the data as a pointer to a C-style array:

processData(&dataVector[0]);
processData(dataArray.data());

I would strongly recommend adding bounds-checking if you are going to do this. Pass a second argument to processData with the size of the array:

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

And if you can abandon C-style pointer/arrays entirely, a better way might be to pass by reference:

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

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

But this only works for vectors, not std::tr1::arrays or any other container. So, taking it one step further, you could use a template that accepts iterators:

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);

The last one works because pointers to C-style arrays can be used as iterators.


In your situation using a vector is the safest choice:

std::vector<uint32> data(123);

The signature of processData should ideally be:

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

However, this one is more frequently used:

void processData(uint32 * bytes, int length);

In both cases you can use the vector:

// 1
processData(data);

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


Not withstanding why you should want to use auto and dumb (as you put it) pointers for the same data to the same name function without overloading, auto_ptr cannot be used on arrays because it calls the wrong sort of delete.

Have a look at this: http://learningcppisfun.blogspot.com/2007/05/custom-deleters-with-smart-pointers.html

Also have a look at this SO question regarding smart pointers to arrays: auto_ptr for arrays


It amuses me that the data_smart variable is the dumbest of the three. That is, when that scope ends, the auto_ptr destructor is going to call delete on its pointer and not delete[] which leads to UB (which is worse than the possible memory leak from data_dumb).

So, the point is don't use auto_ptr for arrays, use vector.

Onto the real question. First, if possible use reference arguments instead of pointer arguments. If this isn't possible use bare pointers and auto_ptr::get() gives access to the underlying pointer.


Ignoring the already HAMMERED don't use auto_ptr on array.

What is the best practice for this without overloading?

It appears your method will not take ownership, so the remaining question is will it be changed?

Having the processData function with a uint32* argument?

processData( uint32* ) Thats one option, but maybe not the best.
processData( uint32[123] ) if your not editing (123 is starting to push some copying).
processData( uint32 &[123] ) by ref and apply const as necessary.

Can I cast the smart pointer to uint32* for this case with .get()?

You can get the pointer content of the smart pointer using get(), it's already 'typed' so no need to cast it.

Aside: Data and manipulation of at such a raw level should be in the one class, you probably don't even need to pass what should be a member variable into a member function.

0

精彩评论

暂无评论...
验证码 换一张
取 消