I have the following structure:

CObject
{
    protected:
        char *mName;
    public:
        CObject(char *n)
        {
            mName=strdup(n);
        }
};

CVector:public CObject
{
    char *mValues[50];
    int mElements;
    public:
        CVector(char *n):CObject(n) {}
};

CMatrix:public CObject
{
    char *mValues[50][50];
    int mLines;
    int mColumns;

    public:
        CMatrix(char *n):CObject(n) {}
};

My main function:

int main()
{
    pV=new CVector("Vector 1");
    pM=new CMatrice("Matrice 1");

    delete pV;
    delete pM;
}

As you can see, i want to explicitly call them.

Also keep in mind that if you need a destructor, you probably also need a copy constructor and copy assignment operator (and possibly a move constructor and move assignment operator for C++11). I'd also avoid a protected data member in favor of a private data member and public const getter. That way CObject absolutely controls mName, and can make assumptions about its integrity (ie. assuming that it's always safe to delete the pointer). For example:

class CObject
{
    char *mName;
public:
    CObject(char *name) : mName(strdup(name)) {}
    CObject(const CObject& rhs) : mName(strdup(rhs.mName)) {}
    CObject(CObject&& rhs) : mName(rhs.mName) { rhs.mName = nullptr;  }
    ~CObject() { delete[] mName; }

    CObject& operator=(const CObject& rhs)
    {
        if (&rhs != this)
        {
            mName = strdup(rhs.mName);
        }
    }

    CObject& operator=(CObject&& rhs)
    {
        mName = rhs.mName;
        rhs.mName = nullptr;
    }

    const char *Name() const { return mName; }
};

Edited 3 Years Ago by deceptikon

why is mName char* instead of std::string? IMO c++ programs shouldn't use char* unless there is a good reason for it. If you used std::string then you won't need a destructor.

Edited 3 Years Ago by Ancient Dragon

why is mName char* instead of std::string?

From previous threads, I can safely conclude that the OP isn't allowed to use such convenient classes yet.

Edited 3 Years Ago by deceptikon

I don't see a destructor at all.

You can define one this way:

~CObject( ) { delete [] mName; }

I tried this, but i get debug assertion failed. From what i've read, it's because my object are destructed for a multiple number of times.

virtual ~CObiect()
{
    if (mNume)
        delete mNume;
}

why is mName char* instead of std::string? IMO c++ programs shouldn't use char* unless there is a good reason for it. If you used std::string then you won't need a destructor.

std::string is STL? because we are not allowed to use it.

@deceptikon, why do i have to implement a copy constructor and to overload the assignement operator when creating a destructor?

I tried this, but i get debug assertion failed. From what i've read, it's because my object are destructed for a multiple number of times.

If you're calling the destructor multiple times, something is very wrong with how you're using the class. Perhaps you're trying to delete the object pointer twice?

std::string is STL? because we are not allowed to use it.

Technically std::string isn't a part of the STL, but it is part of the extensive standard library of which the STL is a subset. If you aren't allowed to use "the STL", std::string is very likely to be implied in that restriction.

@deceptikon, why do i have to implement a copy constructor and to overload the assignement operator when creating a destructor?

Because if you use the default copy constructor and assignment operator, you'll have two objects that refer to the same block of memory. This is a very risky situation that would end up in trying to delete the pointer multiple times (on the destructor call of each object).

This is the rule of 3 (and more recently the rule of 5) intended to handle the management of resources owned by a class as safely and completely as possible.

Edited 3 Years Ago by deceptikon

I've read some time ago the rule of three but i totaly forgot about it. I did understand the need for a destructor if the last two were implemented, but i don't get why it's needed the other way too.

I ussualy implement the copy constructor and overload the assignement operator, but i was solving a problem which didn't have specification to create them, but to correctly implement the destructors. I guess this was the catch(if i do or don't know about this rule).

i was solving a problem which didn't have specification to create them

If it's a school assignment then you don't need to go out of your way, just be aware of the issues. In real code, I'd prefer in most cases where shallow copy is a problem to either implement all of those member functions, or implement a subset of them and disable the rest. For example if you didn't want your class to be copyable, movable, or assignable:

class CObject
{
    char *mName;
public:
    CObject(char *name) : mName(strdup(name)) {}
    CObject(const CObject& rhs) = delete;
    CObject(CObject&& rhs) = delete;
    ~CObject() { delete[] mName; }

    CObject& operator=(const CObject& rhs) = delete;
    CObject& operator=(CObject&& rhs) = delete;

    const char *Name() const { return mName; }
};

The workaround to such behavior prior to C++11 was to make the member functions in question private:

class CObject
{
    char *mName;
public:
    CObject(char *name) : mName(strdup(name)) {}
    ~CObject() { delete[] mName; }

    const char *Name() const { return mName; }
private:
    // Noncopyable. Move semantics were added with C++11, so not necessary to deal with here
    CObject(const CObject& rhs) {}
    CObject& operator=(const CObject& rhs) {}
};

I tried this, but i get debug assertion failed.

My guess is that you shouldn't use delete to deallocate the string's memory which was allocated with strdup (which is not a standard function, btw). Conventionally, it is probably safer to assume that its memory was allocated with malloc, and thus, should be deallocated using the free function:

~CObject() { free(mName); };

Following my tutorial on the Big Five, the proper way to implement the CObject class is as such:

// since strdup is not standard, but very useful, I implement my own (uses 'new'):
char* my_strdup(const char* rhs) {
  if(rhs) {
    std::size_t len = std::strlen(rhs);
    char* result = new char[len + 1];
    std::memcpy(result, rhs, len + 1);
    return result;
  } else
    return nullptr;
};

class CObject
{
  private:
    char* mName;

  public:
    CObject(const char* name = "") : mName(my_strdup(name)) {}

    CObject(const CObject& rhs) : mName(my_strdup(rhs.mName)) {}

    CObject(CObject&& rhs) : mName(rhs.mName) { rhs.mName = nullptr;  }

    // all base-classes must have a virtual destructor:
    virtual ~CObject() { delete[] mName; }

    friend void swap(CObject& lhs, CObject& rhs) {
      char* tmp = lhs.mName;
      lhs.mName = rhs.mName;
      rhs.mName = tmp;
    }

    CObject& operator=(CObject rhs) {
      swap(*this, rhs);
      return *this;
    }

    const char* Name() const { return mName; }
};

My guess is that you shouldn't use delete to deallocate the string's memory which was allocated with strdup (which is not a standard function, btw)

You are correct -- strdup() calls malloc(), not new so the string must call free() not delete[] to free it.

CObject& operator=(const CObject& rhs) = delete;

why did you use const?

CObject& operator=(CObject&& rhs) = delete;

what's up with the double reference?

How are they enabled using "= delete" ? Something like

CObject& operator=(const CObject& rhs) {return *this}

is a way of enabling them?

I tried free instead of delete, no notable difference. Interesting idea implementing your own strdup.

Adding a copy constructor and overloaded assignement operator in CObject base class did solve the problem.

Edited 3 Years Ago by tanatos.daniel

My guess is that you shouldn't use delete to deallocate the string's memory which was allocated with strdup (which is not a standard function, btw).

Good catch, I totally brain farted on that one. ;)

why did you use const?

Why not? Defaulting to const is safer, and in this case there's no reason to have write access to the object being copied.

what's up with the double reference?

That's an rvalue reference from C++11.

How are they enabled using "= delete" ?

I don't understand the question. Using the C++11 =delete syntax tells the compiler not to provide implicit implementations of the member function. In the case of a copy constructor, if you don't write one explicitly, one is provided for you with a shallow copy. Since you don't want a shallow copy, the two options are to write a copy constructor or disable copy construction.

Edited 3 Years Ago by deceptikon

This question has already been answered. Start a new discussion instead.