Skip to content

Commit 6529989

Browse files
authored
Merge pull request #2503 from mikeblome/mb-delegate-init
Refactor a Modern C++ topic into 1 new topic and 1 existing topic in reference node
2 parents c9eb32b + 5403bad commit 6529989

6 files changed

+241
-228
lines changed

.openpublishing.redirection.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,11 @@
740740
"redirect_url": "/cpp/cpp/program-and-linkage-cpp",
741741
"redirect_document_id": false
742742
},
743+
{
744+
"source_path": "docs/cpp/uniform-initialization-and-delegating-constructors.md",
745+
"redirect_url": "/cpp/cpp/initializing-classes-and-structs-without-constructors-cpp",
746+
"redirect_document_id": false
747+
},
743748
{
744749
"source_path": "docs/cpp/linkage-in-names-with-file-scope.md",
745750
"redirect_url": "/cpp/cpp/program-and-linkage-cpp",

docs/cpp/constructors-cpp.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: "Constructors (C++)"
3-
ms.date: "10/17/2019"
3+
ms.date: "11/19/2019"
44
helpviewer_keywords: ["constructors [C++]", "objects [C++], creating", "instance constructors"]
55
ms.assetid: 3e9f7211-313a-4a92-9584-337452e061a9
66
---
@@ -533,7 +533,7 @@ public:
533533
};
534534
```
535535
536-
The object created by the constructors is fully initialized as soon as any constructor is finished. For more information, see [Uniform Initialization and Delegating Constructors](../cpp/uniform-initialization-and-delegating-constructors.md).
536+
The object created by the constructors is fully initialized as soon as any constructor is finished. For more information, see [Delegating Constructors](../cpp/delegating-constructors.md).
537537
538538
## <a name="inheriting_constructors"></a> Inheriting constructors (C++11)
539539
@@ -588,7 +588,7 @@ Derived d4 calls: Base()*/
588588

589589
::: moniker range=">=vs-2017"
590590

591-
**Visual Studio 2017 version 15.7 and later**: The **using** statement in **/std:c++17** mode brings into scope all constructors from the base class except those that have an identical signature to constructors in the derived class. In general, it is best to use inheriting constructors when the derived class declares no new data members or constructors. See also [Improvements in Visual Studio 2017 version 15.7](https://docs.microsoft.com/cpp/overview/cpp-conformance-improvements?view=vs-2017#improvements_157).
591+
**Visual Studio 2017 and later**: The **using** statement in **/std:c++17** mode brings into scope all constructors from the base class except those that have an identical signature to constructors in the derived class. In general, it is best to use inheriting constructors when the derived class declares no new data members or constructors. See also [Improvements in Visual Studio 2017 version 15.7](https://docs.microsoft.com/cpp/overview/cpp-conformance-improvements?view=vs-2017#improvements_157).
592592

593593
::: moniker-end
594594

@@ -636,3 +636,13 @@ int main(){
636636
StorageBox sb3(1, 2, 3, {"myname", "myaddress"});
637637
}
638638
```
639+
640+
## In this section
641+
642+
- [Copy constructors and copy assignment operators](copy-constructors-and-copy-assignment-operators-cpp.md)
643+
- [Move constructors and move assignment operators](move-constructors-and-move-assignment-operators-cpp.md)
644+
- [Delegating constructors](delegating-constructors.md)
645+
646+
## See also
647+
648+
[Classes and structs](classes-and-structs-cpp.md)

docs/cpp/delegating-constructors.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
---
2+
title: "Delegating constructors (C++)"
3+
description: "Use delegating constructors in C++ to invoke other constructors and reduce code repetition."
4+
ms.date: "11/19/2019"
5+
---
6+
7+
# Delegating constructors
8+
9+
Many classes have multiple constructors that do similar things—for example, validate parameters:
10+
11+
```cpp
12+
class class_c {
13+
public:
14+
int max;
15+
int min;
16+
int middle;
17+
18+
class_c() {}
19+
class_c(int my_max) {
20+
max = my_max > 0 ? my_max : 10;
21+
}
22+
class_c(int my_max, int my_min) {
23+
max = my_max > 0 ? my_max : 10;
24+
min = my_min > 0 && my_min < max ? my_min : 1;
25+
}
26+
class_c(int my_max, int my_min, int my_middle) {
27+
max = my_max > 0 ? my_max : 10;
28+
min = my_min > 0 && my_min < max ? my_min : 1;
29+
middle = my_middle < max && my_middle > min ? my_middle : 5;
30+
}
31+
};
32+
```
33+
34+
You could reduce the repetitive code by adding a function that does all of the validation, but the code for `class_c` would be easier to understand and maintain if one constructor could delegate some of the work to another one. To add delegating constructors, use the `constructor (. . .) : constructor (. . .)` syntax:
35+
36+
```cpp
37+
class class_c {
38+
public:
39+
int max;
40+
int min;
41+
int middle;
42+
43+
class_c(int my_max) {
44+
max = my_max > 0 ? my_max : 10;
45+
}
46+
class_c(int my_max, int my_min) : class_c(my_max) {
47+
min = my_min > 0 && my_min < max ? my_min : 1;
48+
}
49+
class_c(int my_max, int my_min, int my_middle) : class_c (my_max, my_min){
50+
middle = my_middle < max && my_middle > min ? my_middle : 5;
51+
}
52+
};
53+
int main() {
54+
55+
class_c c1{ 1, 3, 2 };
56+
}
57+
```
58+
59+
As you step through the previous example, notice that the constructor `class_c(int, int, int)` first calls the constructor `class_c(int, int)`, which in turn calls `class_c(int)`. Each of the constructors performs only the work that is not performed by the other constructors.
60+
61+
The first constructor that's called initializes the object so that all of its members are initialized at that point. You can’t do member initialization in a constructor that delegates to another constructor, as shown here:
62+
63+
```cpp
64+
class class_a {
65+
public:
66+
class_a() {}
67+
// member initialization here, no delegate
68+
class_a(string str) : m_string{ str } {}
69+
70+
//can’t do member initialization here
71+
// error C3511: a call to a delegating constructor shall be the only member-initializer
72+
class_a(string str, double dbl) : class_a(str) , m_double{ dbl } {}
73+
74+
// only member assignment
75+
class_a(string str, double dbl) : class_a(str) { m_double = dbl; }
76+
double m_double{ 1.0 };
77+
string m_string;
78+
};
79+
```
80+
81+
The next example shows the use of non-static data-member initializers. Notice that if a constructor also initializes a given data member, the member initializer is overridden:
82+
83+
```cpp
84+
class class_a {
85+
public:
86+
class_a() {}
87+
class_a(string str) : m_string{ str } {}
88+
class_a(string str, double dbl) : class_a(str) { m_double = dbl; }
89+
double m_double{ 1.0 };
90+
string m_string{ m_double < 10.0 ? "alpha" : "beta" };
91+
};
92+
93+
int main() {
94+
class_a a{ "hello", 2.0 }; //expect a.m_double == 2.0, a.m_string == "hello"
95+
int y = 4;
96+
}
97+
```
98+
99+
The constructor delegation syntax doesn't prevent the accidental creation of constructor recursion—Constructor1 calls Constructor2 which calls Constructor1—and no errors are thrown until there is a stack overflow. It's your responsibility to avoid cycles.
100+
101+
```cpp
102+
class class_f{
103+
public:
104+
int max;
105+
int min;
106+
107+
// don't do this
108+
class_f() : class_f(6, 3){ }
109+
class_f(int my_max, int my_min) : class_f() { }
110+
};
111+
```

docs/cpp/initializing-classes-and-structs-without-constructors-cpp.md

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
---
2-
title: "Initializing classes and structs without constructors (C++)"
3-
ms.date: "10/17/2018"
2+
title: "Brace initialization for classes, structs, and unions"
3+
description: "Use brace initialization with any C++ class, struct or union"
4+
ms.date: "11/19/2019"
45
ms.assetid: 3e55c3d6-1c6b-4084-b9e5-221b151402f4
56
---
6-
# Initializing classes and structs without constructors (C++)
7+
8+
# Brace initialization
79

810
It is not always necessary to define a constructor for a class, especially ones that are relatively simple. Users can initialize objects of a class or struct by using uniform initialization, as shown in the following example:
911

@@ -54,7 +56,110 @@ int main()
5456
}
5557
```
5658
57-
Note that when a class or struct has no constructor, you provide the list elements in the order that the members are declared in the class. If the class has a constructor, provide the elements in the order of the parameters.
59+
Note that when a class or struct has no constructor, you provide the list elements in the order that the members are declared in the class. If the class has a constructor, provide the elements in the order of the parameters. If a type has a default constructor, either implicitly or explicitly declared, you can use default brace initialization (with empty braces). For example, the following class may be initialized by using both default and non-default brace initialization:
60+
61+
```cpp
62+
#include <string>
63+
using namespace std;
64+
65+
class class_a {
66+
public:
67+
class_a() {}
68+
class_a(string str) : m_string{ str } {}
69+
class_a(string str, double dbl) : m_string{ str }, m_double{ dbl } {}
70+
double m_double;
71+
string m_string;
72+
};
73+
74+
int main()
75+
{
76+
class_a c1{};
77+
class_a c1_1;
78+
79+
class_a c2{ "ww" };
80+
class_a c2_1("xx");
81+
82+
// order of parameters is the same as the constructor
83+
class_a c3{ "yy", 4.4 };
84+
class_a c3_1("zz", 5.5);
85+
}
86+
```
87+
88+
If a class has non-default constructors, the order in which class members appear in the brace initializer is the order in which the corresponding parameters appear in the constructor, not the order in which the members are declared (as with `class_a` in the previous example). Otherwise, if the type has no declared constructor, the order in which the members appear in the brace initializer is the same as the order in which they are declared; in this case, you can initialize as many of the public members as you wish, but you cannot skip any member. The following example shows the order that's used in brace initialization when there is no declared constructor:
89+
90+
```cpp
91+
class class_d {
92+
public:
93+
float m_float;
94+
string m_string;
95+
wchar_t m_char;
96+
};
97+
98+
int main()
99+
{
100+
class_d d1{};
101+
class_d d1{ 4.5 };
102+
class_d d2{ 4.5, "string" };
103+
class_d d3{ 4.5, "string", 'c' };
104+
105+
class_d d4{ "string", 'c' }; // compiler error
106+
class_d d5("string", 'c', 2.0 }; // compiler error
107+
}
108+
```
109+
110+
If the default constructor is explicitly declared but marked as deleted, default brace initialization cannot be used:
111+
112+
```cpp
113+
class class_f {
114+
public:
115+
class_f() = delete;
116+
class_f(string x): m_string { x } {}
117+
string m_string;
118+
};
119+
int main()
120+
{
121+
class_f cf{ "hello" };
122+
class_f cf1{}; // compiler error C2280: attempting to reference a deleted function
123+
}
124+
```
125+
126+
You can use brace initialization anywhere you would typically do initialization—for example, as a function parameter or a return value, or with the **new** keyword:
127+
128+
```cpp
129+
class_d* cf = new class_d{4.5};
130+
kr->add_d({ 4.5 });
131+
return { 4.5 };
132+
```
133+
134+
## initializer_list constructors
135+
136+
The [initializer_list Class](../standard-library/initializer-list-class.md) represents a list of objects of a specified type that can be used in a constructor, and in other contexts. You can construct an initializer_list by using brace initialization:
137+
138+
```cpp
139+
initializer_list<int> int_list{5, 6, 7};
140+
```
141+
142+
> [!IMPORTANT]
143+
> To use this class, you must include the [\<initializer_list>](../standard-library/initializer-list.md) header.
144+
145+
An `initializer_list` can be copied. In this case, the members of the new list are references to the members of the original list:
146+
147+
```cpp
148+
initializer_list<int> ilist1{ 5, 6, 7 };
149+
initializer_list<int> ilist2( ilist1 );
150+
if (ilist1.begin() == ilist2.begin())
151+
cout << "yes" << endl; // expect "yes"
152+
```
153+
154+
The standard library container classes, and also `string`, `wstring`, and `regex`, have `initializer_list` constructors. The following examples show how to do brace initialization with these constructors:
155+
156+
```cpp
157+
vector<int> v1{ 9, 10, 11 };
158+
map<int, string> m1{ {1, "a"}, {2, "b"} };
159+
string s{ 'a', 'b', 'c' };
160+
regex rgx{'x', 'y', 'z'};
161+
```
162+
58163

59164
## See also
60165

0 commit comments

Comments
 (0)