Professional C__ - Marc Gregoire [83]
}
void dumpPoints() const
{
for (auto citer = mVecPoints.cbegin();
citer != mVecPoints.cend(); citer += 2) {
cout << "(" << *citer << ", " << *(citer+1) << ")" << endl;
}
}
protected:
vector }; Code snippet from InitializerListCtor\InitializerListCtor.cpp Inside the initializer-list constructor you can access the elements of the initializer-list by using iterators as shown in the previous example. Iterators are discussed in detail in Chapter 12. You can get the number of elements in the initializer-list with the size() method. Objects of PointSequence can be constructed as follows: PointSequence p1 = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; p1.dumpPoints(); try { PointSequence p2 = {1.0, 2.0, 3.0}; } catch (const invalid_argument& e) { cout << e.what() << endl; } Code snippet from InitializerListCtor\InitializerListCtor.cpp The construction of p2 will throw an exception because it has an odd number of elements in the initializer-list. The preceding equal signs are optional and can be left out, for example: PointSequence p1{1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; The C++11 STL has full support for initializer-list constructors. For example, the std::vector container can be initialized by using an initializer-list: std::vector If you use a compiler that does not yet conform to the C++11 standard, one way to initialize the vector is by using several push_back() calls: std::vector myVec.push_back("String 1"); myVec.push_back("String 2"); myVec.push_back("String 3"); Initializer-list constructors are also important in the context of the C++11 uniform initialization feature as explained in the section “Uniform Initialization” in Chapter 9. Initializer lists are not limited to constructors and can also be used with normal functions as explained in Chapter 9. In-Class Member Initializers C++11 allows member variables to be initialized directly in the class definition. For example: #include class MyClass { protected: int mInt = 1; std::string mStr = "test"; }; The only way you could initialize mInt and mStr before C++11 was within a constructor body, or with a ctor-initializer as follows: #include class MyClass { public: MyClass() : mInt(1), mStr("test") {} protected: int mInt; std::string mStr; }; Before C++11, only static const integral member variables could be initialized in the class definition. For example: #include class MyClass { protected: static const int kI1 = 1; // OK static const std::string kStr = "test"; // Error: not integral type static int sI2 = 2; // Error: not const const int kI3 = 3; // Error: not static }; Delegating Constructors Delegating constructors allow constructors to call another constructor from the same class. However, this call cannot be placed in the constructor body; it should be in the constructor initializer. Following is an example: SpreadsheetCell::SpreadsheetCell(const string& initialValue) : SpreadsheetCell(stringToDouble(initialValue)) { } When this string constructor (the delegating constructor) is called, it will first delegate the call to the target, double constructor. When the target constructor returns, the body of the delegating constructor will be executed. Make sure you avoid constructor recursion while using delegate constructors. For example: class MyClass { MyClass(char c) : MyClass(1.2) { } MyClass(double d) : MyClass('m') { } }; The first constructor will delegate to the second constructor, which delegates back to the first one. The behavior of such code is undefined by the standard and depends on the compiler. Summary of Compiler-Generated Constructors The compiler will automatically generate a 0-argument constructor and a copy constructor for every class. However, the constructors you define yourself replace these constructors according to the following rules: IF YOU DEFINE . . . . . . THEN THE COMPILER GENERATES . . . . . . AND YOU CAN CREATE AN OBJECT . . . [no constructors] A 0-argument