Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-pointer class member: how good is it? [duplicate]

Tags:

c++

c++14

I have a non-pointer class member that I need to initialize in the constructor:

class Alerter {

protected:
  Timer timer;

public:
  Alerter(int interval);
};

and then

Alerter::Alerter(int interval) {
    timer = createTimer(interval);
}

(simplified code just to demonstrate the problem).

I have some doubts and concerns that timer probably is first created using its parameter-less constructor and later that instance is overwritten by the contents that the createTimer function returns.

How good is the approach? The possible answers could be:

  • The "empty timer" created by its parameter-less constructor is not actually created, as the compiler is smart enough to find we have never referenced it before overwriting the value.
  • The empty timer is created, but this is OK as well-written code should support a cheap parameter-less constructor for throwaway instances.
  • It's better to use pointers.

Which of these assumptions (or maybe something else) is the most correct?

like image 287
Audrius Meškauskas Avatar asked May 31 '18 09:05

Audrius Meškauskas


People also ask

Can a class have itself as a member?

No, because the object would be infinitely large (because every Node has as members two other Node objects, which each have as members two other Node objects, which each... well, you get the point). Save this answer.

What is non pointer C++?

Non-pointers are objects that, well, hold the actual type. int i; is a single integer. char name is a single character and so on. Pointers are objects that contains addresses to other objects.

Why do we use pointers to objects in C++?

Pointers are used for file handling. Pointers are used to allocate memory dynamically. In C++, a pointer declared to a base class could access the object of a derived class. However, a pointer to a derived class cannot access the object of a base class.


2 Answers

timer is first default-constructed and then assigned. Of course you can make assumption about how cheap a Timer is to default-construct or about compiler optimization, but here you don't need this since can prevent default-construction by using an initializer list:

Alerter::Alerter(int interval) : timer(createTimer(interval)) { }

This will work, unless your Timer class is copy-assignable but not copy-constructible, which would be weird.

like image 138
Holt Avatar answered Nov 15 '22 07:11

Holt


Both constructor will be called (and then the assignment as well).

I have extended your example:

class Timer
{
  public:
    Timer() : m_interval(0)
    {
        std::cout << "Constructor withOUT parameter has been called." << std::endl;
    }

    Timer(int interval) : m_interval(interval)
    {
        std::cout << "Constructor with parameter has been called." << std::endl;
    }

    Timer(const Timer& timer)
    {
        std::cout << "Copy constructor has been called." << std::endl;
        m_interval = timer.m_interval;
    }

    Timer(Timer&& timer)
    {
        std::cout << "Move constructor has been called." << std::endl;
        m_interval = timer.m_interval;
    }

    void operator=(Timer&& timer)
    {
        std::cout << "Move assignment has been called." << std::endl;
        m_interval = timer.m_interval;
    }

  private:
    int m_interval;
};

class Alerter
{
  protected:
    Timer timer;

  public:
    Alerter(int interval)
    {
        timer = Timer(interval);
    }
};

... 
Alerter alerter(12);
...

Output:

Constructor withOUT parameter has been called.
Constructor with parameter has been called.
Move assignment has been called.

And answering your question. It is better if the member variables are initialized only once but this is not a hard rule. For example, if the Timer is a very complex object but the default constructor creates just a dummy object, it is not too expensive and acceptable.

So, if you can avoid, better to avoid. Using pointer is a possible solution, but note the followings:

  • Better to use std::unique_ptr to avoid memory leaks.
  • This surely will require a heap memory allocation operation. If the objects are anyway on the heap (e.g. uses complex internal structures, like STL containers), it is not a problem. But if the objects are quite simple and the main use case is to create them as local objects, this can cause quite a big performance penalty. This should be a conscious design decision.
like image 27
Tibor Takács Avatar answered Nov 15 '22 05:11

Tibor Takács