Class construction with initial values

后端 未结 8 1829
别那么骄傲
别那么骄傲 2020-12-06 20:05

I\'m new to C++, and the whole idea of classes - I\'m still reading a book to try and learn. The book I\'m reading says that when I construct a class, I can assign default v

相关标签:
8条回答
  • 2020-12-06 20:12

    The difference is that the compiler will always initialize all members (in declaration order) prior to the first user-defined constructor statement. In the case of a char and an int, which are both primitive types, 'initialization' actually means 'no initialization' here. However, if you have a member with a constructor that does some actual work, this constructor will be called upfront - if you do

    foo::foo() {
        myComplexMember = MyComplexClass(42);
    }
    

    the compiler did already invoke the MyComplexClass default constructor before your code got called, which is a waste of resources (and a compiler error if the default ctor is not accessible).

    By using an initialization list, you can customize the default initialization and avoid doing things for nothing. Obviously, this is the way to go.

    0 讨论(0)
  • 2020-12-06 20:15

    I would get into the habit of using initialisation lists. They will not suffer from problems when somebody changes a char to some object where the default constructor is called first, and also for const correctness for the const values!

    0 讨论(0)
  • 2020-12-06 20:23

    If the members had non-trivial constructors, in the code below first the default constructors would be called, then the assignments would be executed, while in the code above they would be initialized only one time. So yes, there may be a performance issue.

    There is also a practical issue: if they are const, references, or don't have default constructors, you can't use the version below.

    0 讨论(0)
  • 2020-12-06 20:28

    Well, this is a typical FAQ question: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.6

    In your case, using char and int there are no differences.

    General rule: use initialization list as possibile, there are very few cases when you can prefer assignment, for example for improve readability:

    MyClass::MyClass
    {
      a = b = c = d = e = f = 0;
    }
    

    is better than

    class MyClass::MyClass : a(0), b(0), c(0), d(0), e(0), f(0) { }
    
    0 讨论(0)
  • 2020-12-06 20:30

    There is a subtle, but important difference between those two options. What you have at the top is called a member initialization list. When the object is created, the members in this list are initialized to whatever you put in the parenthesis.

    When you do assignment in the constructor body, the values are being first initialized, and then assigned. I'll post a short example below.

    Example 1: Member initialization

    class foo 
    {
    public:
       foo(char c, int i);
    
    private:
       char exampleChar;
       int exampleInt;
       Bar exampleBar;
    };
    
    foo::foo(char c, int i):
        exampleChar(c),
        exampleInt(i),
        exampleBar()     //Here, a bar is being default constructed
    {
    }
    

    Example 2: Constructor Assignment

    class foo 
    {
    public:
       foo(char c, int i, Bar b);
    
    private:
       char exampleChar;
       int exampleInt;
       Bar exampleBar;
    };
    
    foo::foo(char c, int i, Bar b):
        //exampleChar(c),
        //exampleInt(i),
        //exampleBar()  
    {
        exampleChar = c;
        exampleInt = i;
        exampleBar = someOtherBar;  //Here, a bar is being assigned
    }
    

    This is where it gets interesting. Note that exampleBar is being assigned. Behind the scenes, a Bar is actually first being default constructed, even though you did not specify that. Furthermore, if your Bar is more complicated then a simple struct, you will need to be sure to implement the assignment operator in order for you to initialize it in this way. Even furthermore, in order to initialize the Bar from another Bar from the member initialization list, you must implement the copy constructor!

    Example 3: Copy constructor used in member init

    class foo 
    {
    public:
       foo(char c, int i, Bar b);
    
    private:
       char exampleChar;
       int exampleInt;
       Bar exampleBar;
    };
    
    foo::foo(char c, int i, Bar b):
        //exampleChar(c),
        //exampleInt(i),
        exampleBar(b)     //Here, a bar is being constructed using the copy constructor of Bar
    {
        exampleChar = c;
        exampleInt = i;
    }
    
    0 讨论(0)
  • 2020-12-06 20:33

    The first way, by doing : exampleChar(c), exampleInt(i) is called an initializer list.

    If you do it the second way, the two variables are default constructed first1, then you assign them a value. (When the actual body of the constructor is entered, anything that hasn't been initialized by the initializer list is default constructed.) This is a waste of time because you're just overwriting the values anyway. For small types like int or char this isn't a big deal, but when those member variables are large types that would take lots of cycles to construct, you definitely want to use the initializer list.

    The second way won't waste time giving them a default value and then overwriting it - it will set their values directly to that value you give it (or call the right constructor if the member is an object).

    You can see what we mean by doing this:

    class MyClass {
    public:
        int _i; // our data
    
        // default constructor
        MyClass() : _i(0) { cout << "default constructor"; }
    
        // constructor that takes an int
        MyClass(int i) : _i(i) { cout << "int constructor"; }
    
        // assignment operator
        void operator=(int i) { _i = i; cout << "assignment operator"; }
    };
    
    class OtherClass {
    public:
        MyClass c;
    
        OtherClass() {
            c = 54;
        }
    };
    
    OtherClass oc;
    

    You'll see that

    default constructor
    assignment operator
    

    is printed. That's two function calls which, for other classes, could be expensive.

    If you change the constructor of OtherClass to

    OtherClass() : c(54) {   }
    

    You'll see that

    int constructor
    

    is printed. Just one call compared to two. This is the most efficient way.

    Initializer lists are also a must when you

    1. have types that have no default constructor. You have to call the right constructor in the initializer list.

    2. have a const member that you want to give some value (rather than just have permantently the default value

    3. have a reference member. You must use initializer lists on these.

    tl;dr: do it because it's at least as fast but never slower than the other way, and sometimes far faster.

    1 For built in types like int and char, they are actually not constructed at all; they just have the value of whatever memory they happen to have had previously.

    0 讨论(0)
提交回复
热议问题