Domanda

I am moving from structural C to OOP C++ and I frequently found a special use of ":" symbol as an operator when declaring/defining constructors in C++. I roughly understood the use of this style but somebody explain me the exact programming technique with this constructor definition.

e.g.: 1

class time_stamp
{
public:
    time_stamp(time &t_time)
        : m_time(t_time)
    {}

    ~time_stamp()
    {
        m_time.update(); // as soon as I'm destroyed, update the time
    }
private:
    time &m_time;
};

e.g.: 2

class threaded_class
{
public:
    threaded_class()
        : m_stoprequested(false), m_running(false)
    {
        pthread_mutex_init(&m_mutex);
    }

    ~threaded_class()
    {
        pthread_mutex_destroy(&m_mutex);
    }

    /** Some other member declarations */

}

Please explain me use of ":" in below lines of codes from above 2 examples time_stamp(time &t_time) : m_time(t_time){} and

threaded_class(): m_stoprequested(false), m_running(false)
{
   pthread_mutex_init(&m_mutex);
}
È stato utile?

Soluzione

The colon character : is used to denote the constructor member initializer list. This is the place where you can initiailze members of a class or call a base class constructor.

C++ Standard n3337 12.6.2 § 3:

A mem-initializer-list can initialize a base class using any class-or-decltype that denotes that base class type.

C++ Standard n3337 12.6.2 § 7:

The expression-list or braced-init-list in a mem-initializer is used to initialize the designated subobject (or, in the case of a delegating constructor, the complete class object) according to the initialization rules of 8.5 for direct-initialization.

Example:

class Foo {
   int a;
};

If you would like integer a to have determined value after call to constructor is made you have to give a this value in constructor. There are two options:

  • in constructor body

    Foo::Foo() {
        a = 70;
    }
    
  • in it's member initializer list

    Foo::Foo() : a( 70) {
    }
    

Initialization via a member initilization list should be preferred

It is always legal, is never less efficient than assignment inside the body of the constructor, and is often more efficient. The very important thing about initialization list is that it allows to direct initialize class member omitting a default construction of a member being subject to such a process.

As Scott Myers pointed out in his "Effective C++", if you fail to specify an initialization argument for class member, it's default constructor will be called. When you later perform an assignment to it inside your class constructor, you will call operator= on member variable. That will total two calls to member functions: one for the default constructor and one more for the assignment. You can omit a first call by specifying an initializer. Also as Scott Myers pointed out in his "Effective C++" : "from a purely pragmatic point of view, there are times when the initialization list must be used. In particular, const and reference members may only be initialized, never assigned".

A trap

(At least) Equally important thing is that members are not initialized in order of their appearance in initialization list but in order of declaration in class. Remember this to avoid errors like

/* trying to allocate very large block of memory
   as a result of initializing a vector with
   uninitialized integer: std::vector<int> v( N)
*/
class SearchEngine {
    std::vector<int> v;
    int N;
    explicit SearchEngine( std::vector<int> const& keys)
                  : N( keys.size()), v( N), {

C++ Standard n3337 8.5.4 § 1:

List-initialization is initialization of an object or reference from a braced-init-list. Such an initializer is called an initializer list, and the comma-separated initializer-clauses of the list are called the elements of the initializer list. An initializer list may be empty. List-initialization can occur in direct-initialization or copy- initialization contexts; list-initialization in a direct-initialization context is called direct-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization. [ Note: List-initialization can be used — as the initializer in a variable definition (8.5)

— as the initializer in a new expression (5.3.4)

— in a return statement (6.6.3)

— as a function argument (5.2.2)

— as a subscript (5.2.1)

— as an argument to a constructor invocation (8.5, 5.2.3)

— as an initializer for a non-static data member (9.2)

— in a mem-initializer (12.6.2)

— on the right-hand side of an assignment (5.17)

[ Example:

int a = {1};

std::complex z{1,2};

new std::vector{"once", "upon", "a", "time"}; // 4 string elements

f( {"Nicholas","Annemarie"} ); // pass list of two elements

return { "Norah" }; // return list of one element

int* e {}; // initialization to zero / null pointer

x = double{1}; // explicitly construct a double

std::map anim = { {"bear",4}, {"cassowary",2}, {"tiger",7} };

— end example ] — end note ]

Altri suggerimenti

It's for member initialisation. This is the only place you can init members without them being default initialised.

If you do it within curly braces, the default constructor for members has already been invoked and your assigning new value to it. With the colon syntax, you decide how members are initialised (in terms of value fortrivial types and in terms of constructor with non trivial ones).

Initialization list. It is useful when you want to initialize the member objects right after construction. And you have to use it when a member object has a not a default constuctor.

And it's not just another way to initialize members, sometimes you have to use it, and most of the time you should use it to keep the code consistence.

Here is an example for a situation that you have to use it:

struct A
{
   const X x; // X has not default constructor

   A() : x(some_value) {}
};

Even if a member object has a default constructor, you should initialize it by initialization-list to avoid redundant construction.

struct A
{
   string x;

   A() : x("Hello") {}
};

In the above case, if you assign "Hello" inside the constructor's body, then you made an unnecessary call to string::string() and then x = "Hello";, which it can be replaced by just one call to string::string("Hello").

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top