3

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!

vls
  • 243
  • 1
  • 3
  • 11
  • 10
    I'm afraid that you're already screwed since using auto_ptr to point at an array is totally reason enough to light you on fire. – Edward Strange Dec 10 '10 at 23:04
  • 2
    auto_ptr is a classic example of design by committee. Ugh. Try boost::scoped_ptr and boost::shared_ptr instead. – Mark Storer Dec 10 '10 at 23:12
  • 2
    No. Ignoring the array usage for 1 second. std::auto_ptr<> is used to indicate transfer of ownership semantics so you are using it incorrectly. It is usually specified in a methods API to indicate that if you pass an object to the method the method is going to take ownership of that object. In this situation you should be using data_fix (and preferably pass by reference to processesData()) – Martin York Dec 10 '10 at 23:42

8 Answers8

5

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.

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
1

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().

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • Well, it can be done with a templates, which isn't technically "overloading". But then, of course, they've still got the serious issue of their attempt to use the wrong kind of smart pointer. – Edward Strange Dec 11 '10 at 00:02
1

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.

LavaScornedOven
  • 737
  • 1
  • 11
  • 23
  • 2
    "you'll end up with memory leaks" should be "you'll end up with undefined behavior" – John Dibling Dec 10 '10 at 23:17
  • I agree with you about terminology from the standard, but undefined behavior is phrase used for anything that is out of the scope of the standard. Using "memory leaks" as substitute is not most formal way of expressing undefined behavior, but in this case, I find it common sense. – LavaScornedOven Dec 10 '10 at 23:33
  • Sorry, Vedran, but you're wrong. You're going to confuse the memory manager. Simple fact is that calling 'delete' when you should be calling 'delete[]' can, and often will, be as bad as calling free() on something you newed. A confused memory manager results in crap that is the very definition of "undefined", such as, "WTF is my code crashing when I try to create a vector in this seemingly unrelated function I just called but only when I'm running a release build on the second tuesday of the month?" – Edward Strange Dec 11 '10 at 00:04
  • So I see. Than you for correcting me. I must admit I never actually had problems like this (because I don't use auto_ptr for arrays :) ), but I guess its time to dust off some of Tanenbaum books. – LavaScornedOven Dec 11 '10 at 00:14
1

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.

Nate
  • 18,752
  • 8
  • 48
  • 54
0

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

Community
  • 1
  • 1
Tony
  • 9,672
  • 3
  • 47
  • 75
0

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.

Chris Hopman
  • 2,082
  • 12
  • 11
0

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.

Greg Domjan
  • 13,943
  • 6
  • 43
  • 59
0

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());
StackedCrooked
  • 34,653
  • 44
  • 154
  • 278
  • Okay, for the fast solution, I'll just go with a own wrapper class for my data, which is created statically and constructs / destructs the data buffer. For the not-so-high-speed parts of my application, I'll use the vector. – vls Dec 16 '10 at 18:13