NonCopyable everywhere ?

Started by
14 comments, last by Khatharr 8 years, 5 months ago

Hodgman completely resumed why I asked the question, I just use macro and not a class to avoid multiple inheritance for that like that :


#define NON_COPYABLE( Class )\
  public:\
    Class( const Class& ) = delete;\
    Class& operator = ( const Class& ) = delete;

And use it like that :


class A
{
  NON_COPYABLE( A )
};
I also use a NoCreate class, which has private, non-implemented constructors and destructors

You surely do that to have a static function Create and a destroy function which destroys himself.

The problem is derived class has to use also NoCreate or they could be instanciate without Create function.

Maybe you have a way to avoid this issue of NoCreate, because using delete on the destructor remove the possibility to inherit.

I sent a mail to iso-cpp to ask if a "forbid" keyword could be an idea as a new keyword for c++ to remove this issue.

Advertisement


You surely do that to have a static function Create and a destroy function which destroys himself.

The problem is derived class has to use also NoCreate or they could be instanciate without Create function.

Maybe you have a way to avoid this issue of NoCreate, because using delete on the destructor remove the possibility to inherit.

I sent a mail to iso-cpp to ask if a "forbid" keyword could be an idea as a new keyword for c++ to remove this issue.

I belive he is rather doing that to have a class that really can never ever be instantiated, like a class that has only static methods.

If the constructor is private, derived classes also cannot be instantiated eigther, since the derived class needs to access the parents class ctor in order to construct itself.

I use it for C-style ADT's, to cleanly separate interface and implementation, and to force the user to call the factory functions instead of new/delete directly.
To steal Wikipedia's Example:
//Header:
struct Stack : private NoCreate          // can't create/destroy this yourself - it's an ADT
{
  static Stack* Create();        // creates a new empty stack instance
  static void Destroy(Stack*);   // destroys a stack instance
  void Push(void*);              // adds an item at the top of the stack
  void* Pop();                   // removes the top item from the stack and returns it
  bool Empty();                  // checks whether stack is empty
};

//CPP:
struct StackImpl
{
  std::stack<void*> data;
};

Stack* Stack::Create()
{
  return (Stack*)new StackImpl();
}
void Stack::Destroy(Stack* s)
{
  delete (StackImpl*)s;
}
void Stack::Push(void* p)
{
  StackImpl& m = (StackImpl*)this;
  m.data.push_back(p);
}
void* Stack::Pop()
{
  StackImpl& m = (StackImpl*)this;
  return m.data.pop_back();
}
bool Stack::Empty()
{
  StackImpl& m = (StackImpl*)this;
  return m.data.empty();
}

I just use macro and not a class to avoid multiple inheritance for that like that :

Multiple inheritance is bad when you're inheriting multiple concrete classes, or interfaces that clients will cast your class to.
NonCopyable doesn't fit those two categories, so it almost doesn't count as inheritance - it's more of a "mixin".
I actually recommend using private inheritance for cases like this (as in my example above with NoCreate) -- it's not an interface or type which your users should ever have as a pointer type, so it should be private.

Hodgman completely resumed why I asked the question, I just use macro and not a class to avoid multiple inheritance for that like that :





#define NON_COPYABLE( Class )\
  public:\
    Class( const Class& ) = delete;\
    Class& operator = ( const Class& ) = delete;
And use it like that :


class A
{
  NON_COPYABLE( A )
};

I also use a NoCreate class, which has private, non-implemented constructors and destructors

You surely do that to have a static function Create and a destroy function which destroys himself.
The problem is derived class has to use also NoCreate or they could be instanciate without Create function.
Maybe you have a way to avoid this issue of NoCreate, because using delete on the destructor remove the possibility to inherit.
I sent a mail to iso-cpp to ask if a "forbid" keyword could be an idea as a new keyword for c++ to remove this issue.


I would recommend NOT putting "public" in that macro, as then the macro can unintentionally change access semantics when used. It's also not really needed as if the macro is put in a public/protected block you'll just get a different error than if they were in public, and it doesn't break the non-copyable status of the class.

I reintroduced the NonCopyable class after this discussion.

I use it for C-style ADT's, to cleanly separate interface and implementation, and to force the user to call the factory functions instead of new/delete directly.

The good thing about using Create/Destroy like that is to remove the potential crash when using DLL as plugin too.

Since delete on the destructor of the call cause class not possible to be inherited, the only way is to inherit NoCreate class on all derived ?

The issue with that is if you don't inherit from NoCreate, then you can instanciate using new/delete, it's why I sent idea for a "forbid" keyword.

Because the first class is called NonCopyable it's more consistent to call it NonCreatable to me.

Does NonCreatable contains NonCopyable too ?


class NonCopyable
{
protected:
  inline NonCopyable(){}

private:
  NonCopyable( const NonCopyable& );
  NonCopyable& operator = ( const NonCopyable& );
};

class NonCreatable
{
private:
  NonCreatable();
  NonCreatable( const NonCreatable& );
  NonCreatable& operator = ( const NonCreatable& );
};

I use NonCopyable thoroughly in my engine for a few reasons
- it's possible to implement copying but YAGNI, so I'll only do it when I need it rather than needlessly writing unused code. I find YAGNI to be an important way to not end up in permanent engine-development hell.
- It's possible to implement but would be a significant performance cost, and there's probably a better way to do whatever it is that is making you think that you need a copy, so I'm discouraging you.
- multiple copy semantics are equally valid (e.g. deep/shallow), and I don't want an automatic opetator to have vague or unexpected behaviour, so copying via explicit named functions is available instead.
- Copying *could* be possible, but by assuming that it can't happen, I can simplify the code design (e.g. maybe it means I can avoid reference counting), plus YAGNI.

FWIW, I also use a NoCreate class, which has private, non-implemented constructors and destructors smile.png

Sure, it doesn't bother me in application code, but when I find it in APIs it makes my eye twitch.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

This topic is closed to new replies.

Advertisement