tags:

views:

162

answers:

5

I encounters a problem where I want to have a class in which its behavior can be customized by another class, for example, Foo's constructor accepts a parameter of some type of class:

class Bar { //The default class that define behavior
};

template <typename T = Bar>
class Foo {
  public:
    Foo(T* t = 0) t_(t) {
      if (t_ == 0) t_ = new T();
    }
    ~Foo() { 
      delete t_;
    }
}

Now if someone use Foo in a client code:

Foo foo;

Everything is fine. But, if we want to supply the custom class:

class Bar1 { };

Foo<Bar1> foo(new Bar1()); // This is OK
Bar1 b;
Foo<Bar1> foo(&b); // Error, b is not dynamically allocated

Is there any design pattern I can use to prevent this kind of mistakes? Or, is there any techniques or semantics where the user of Foo class can choose/specify who owns the bar object? So for example, the above Foo destructor can be like this:

    ~Foo() { 
      if (t_ is owned by this object) delete t_;
    }

Bar or Bar1 or any class passed as t in Foo(T* t) might be a big object, so if it is possible I rather not to pass it by value.

Update: What I have in mind is for the user to be able to do something like:

Foo foo(new Bar(1, 2, etc..));
//or this:
Bar bar(1, 2, etc..);
Foo foo(bar);

But if bar is a big object (for example, contains an array), then it would be inefficient to pass bar by value. The only way is to have bar passed by reference or pointer, but I also want the user to be able to use bar with parameterized constructor, and hence my confusion began. Regarding to the auto variable scope, it would be safe if a user do something like:

int main() {
  Bar1 bar1(1,2,3);
  Foo foo(&bar1);
  return 0;
}

As long as Foo does not delete bar1.

+4  A: 

Use a smart pointer and you won't have to worry about memory management. If you really have to, I guess you should have the user of your class handle the memory they pass in, your class should be only responsible for deleting the objects that it created.

You have to consider if you really need to use pointers. A simple design like this one:

template <typename T = Bar>
class Foo1 {
  T t_;
  public:
    Foo1() {
    }
    Foo1(const T& t) : t_(t) {
    }
};

might be all you need. There's nothing wrong with using it like this:

int main() {
  Bar1 bar1(1,2,3);
  Foo1 foo(bar1); // specify custom behavior
  Foo1 foooo; // get default behavior
  return 0;
}

If you really need pointers, Dan provided a good answer using std::auto_ptr<>. You can guarantee you always have an object created with something like:

template <typename T = Bar>
class Foo2 {
  std::auto_ptr<T> t_;
  public:
    Foo2() : t_(std::auto_ptr<T>(new T)) { }
    Foo2(std::auto_ptr<T> t) : t_(t) {
      if (t_.get() == 0) t_ = std::auto_ptr<T>(new T);
    }
};

Then you can call it like this:

std::auto_ptr<Bar1> s(new Bar1());
Foo2<Bar1> foo(s);
Foo2<Bar1> foooo;

As for the user passing the address of a stack object to a smart pointer, that's really the user's fault for doing something wrong. The program will crash as soon as the auto_ptr tries to delete the object.

Firas Assaad
+1  A: 

Short answer, No.

For any arbitrary type T to be used such as Bar1 then you have to simply accept that your class's users may write code that explodes.

However if you only construct the instance of type T when you construct the class, instad of taking a pointer to an instance you could in Foo's code construct the instance of Bar1 using a Create function. But then you can't pass arguments to the constructor.

class Bar
{
  public: static Bar * Create()
}

template <typename T>
class Foo
{
  public: static Foo<T>* Create() { return new Foo<T>(T::Create()); } 
}
mat_geek
+5  A: 

You can't detect the difference between stack and heap allocation (the standard doesn't even mention a stack), but that's not the issue here.

Foo should not be deleting things that it does not own. If it wants a copy for itself then it should just make a copy:

template <class T = Bar>
class Foo
{
  T t_;
public:
  Foo() {}
  Foo(const T& t) : t_(t) {}
};

Or if you need it to be a pointer:

template <class T = Bar>
class Foo
{
  T* t_;
public:
  Foo() : t_(new T()) {}
  Foo(const T& t) : t_(new T(t)) {}
};

You can't just go around deleting things that people give you, and that's regardless of whether you know if it's stack or heap allocated. What if your Foo goes and deletes it but the calling code still wants to use it? Or what if that same object is passed into two of your Foo objects and they both delete it?

Your option is either to make a copy, or not delete it.

An alternative to doing the copy would be to mark whether you are using your own or someone else's:

template <class T = Bar>
class Foo
{
  T* t_;
  bool owned;
public:
  Foo() : t_(new T()), owned(true) {}
  Foo(T* t) : t_(t), owned(false) {}
  ~Foo() { if (owned) delete t_; }
};

In case you are wondering, t is passed by const-ref in my previous solutions, not by value, so there is no excessive expense, although there could be an expense in the copying.

Peter Alexander
`std::auto_ptr<>` can be useful in passing around ownership of a pointer.
Dan
It can, but the problem still remains that the calling code has no idea that `Foo` is stealing its data.
Peter Alexander
Sure it does: calling code puts its object in a `std::auto_ptr<>` right after calling `new` as passes *that* to client code instead of a raw pointer.
Dan
Actually what I want is for the caller to surrender the ownership of the T object to the Foo object. Marking the ownership is a good idea, but it adds another extra parameter. Perhaps I can provide two constructor? Foo(T* t); // this is for the dynamic-allocated Foo(T // this is for autos
leiiv
@leiiv: `std::auto_ptr<>` does exactly that.
Dan
leiiv
@Poita: I do want Foo to have a copy for itself, hence this line: if (t_ == 0) t_ = new T();. But I also want to give possibility for the user of Foo to supply it with a custom object, which may have a constructor with parameters/arguments.
leiiv
A: 
sth
What i want to find out is if it is possible for the user to explicitly decide the ownership of the passed pointer, instead of the Foo class.
leiiv
+1  A: 

To clarify my comments in Poita_'s answer:

template <typename T = Bar> 
class Foo { 
    T& t_;
    std::auto_ptr<T> pT_; 

public: 
    Foo(T& t) : t_(t) { 
    }
    Foo(std::auto_ptr<T> pT) : pT_(pT), t_(*pT) { 
    } 

    T& getT() const {
        T* pT = pT_.get();
        if (pT != NULL)
            return *pT;
        return t_;
    }
};

You can then use it as follows:

std::auto_ptr<Bar> pBar(new Bar());
Foo<Bar> foo0(pBar);
Bar& b0 = foo0.getT();

Bar b;
Foo<Bar> foo1(b);
Bar& b1 = foo1.getT();

As this code shows, you should (almost) always place the result of new into some object which manages the memory. std::auto_ptr<> will pass ownership of the pointer to Foo. Alternatively, you could pass the pointer directly in the constructor, but that makes it easier to call Foo(&bar).

Dan
The ability to pass references brings up a good question, do you really need to allow the users the pass pointers? It's better not to use them if you could get away with it.
Firas Assaad
Is it possible to supply both constructor arguments with default value? As far as I know, the problem of having reference as data member is it hard to deal with it if you want to use default value at constructor. Do you know any tricks for that?
leiiv
@Firas: I think references are considerably underused in C++. Too many people that grew up on C?
Dan