-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathA Tour of C++=Bjarne Stroustrup;Note=Erxin.txt
475 lines (301 loc) · 11.7 KB
/
A Tour of C++=Bjarne Stroustrup;Note=Erxin.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
A Tour of C++=Bjarne Stroustrup;Note=Erxin
# User defined types
- declaration is a statement that introduces an entity into the program
A type defines a set of possible values and a set of operations (for an object).
An object is some memory that holds a value of some type.
A value is a set of bits interpreted according to a type.
A variable is a named object.
- bool, char, int, double
int, 4 bytes
double, 8 bytes
- single quote (') as a digit separator. For example, π is about 3.14159'26535'89793
- logic
x&y // bitwise and
x|y // bitwise or
x^y // bitwise exclusive or
~x // bitwise complement
x&&y // logical and
x||y // logical or
!x // logical not (negation)
- initialization
complex<double> z = 1;
complex<double> z2 {1.0, -2.0};
The = form is traditional and dates back to C, but if in doubt, use the general {}-list form.
- We use auto where we don’t have a specific reason to mention the type explicitly.
- scope and lifetime
+ local scope
+ class scope
+ namespace scope
- constants
+ const, the value of a const can be calculated at runtime and promise not to change
+ constexpr, evaluated at compile time. The value of a constexpr must be calculated by the compiler
- C++ also offers a simpler for-statement, called a range-for-statement
for (auto x : {10,21,32,43,54,65})
cout << x << '\n';
- There is only one nullptr shared by all pointer types
- The advice here is a subset of the C++ Core Guidelines
+ Don’t use the built-in features exclusively or on their own. On the contrary, the fundamental (built-in) features are usually best used indirectly through libraries, such as the ISO C++ standard library
+ Focus on programming techniques, not on language features
+ You don’t have to know every detail of C++ to write good programs
+ A function should perform a single logical operation
+ keep function short
+ evaluated at compile time, declare it constexpr
+ Minimize the scope of a variable
+ Keep common and local names short, and keep uncommon and nonlocal names longer
+ Avoid similar-looking names;
+ Avoid ALL_CAPS names
+ Prefer the {}-initializer syntax for declarations with a named type;
+ Use unsigned for bit manipulation only
+ Use nullptr rather than 0 or NULL
+ Don’t declare a variable until you have a value to initialize it with
+ Don’t say in comments what can be clearly stated in code;
# User defined types
- Structures
struct name{
type field_name;
};
- classes, public private and default private
class name{
type field_name;
};
- union is a struct in which all members are allocated at the same address
union name{
type field0;
type field1;
};
- enumerations, ser-defined type for which we can enumerate the values:
enum class Color { red, blue, green };
enum class Traffic_light { green, yellow, red };
Color col = Color::red;
Traffic_light light = Traffic_light::red;
+ init value
Color x = Color{5}; // OK, but verboseColor
y {6}; // also OK
+ The “plain” enums have been in C++ (and C) since the earliest days
- advice
+ Represent the distinction between an interface and an implementation using a class
+ A struct is simply a class with its members public by default;
+ Avoid “naked” unions; wrap them in a class together with a type field
+ Use enumerations to represent sets of named constants;
+ prefer class enums over plain enums
+ define operations on enumerations for safe and simple use
# Modularity
- The first and most important step is to distinguish between the interface to a part and its implementation.
double sqrt(double); // the square root function takes a double and returns a double
class Vector
{
public:
Vector(int s);
double& operator[](int i);
int size();
private:
double* elem; // elem points to an array of sz doubles
int sz;
};
- separate compilation, C++ supports a notion of separate compilation where user code sees only declarations of the types and functions used.
- old way, if you #include header.h in 101 translation units, the text of header.h will be processed by the compiler 101 times
- express the Vector and sqrt_sum() example from §3.2 using modules
// file Vector.cpp:
module; // this compilation will define a module
// ... here we put stuff that Vector might need for its implementation ...
export module Vector; // defining the module called "Vector"export
class Vector {
public:
Vector(int s);
double& operator[](int i);
int size();
private:
double* elem; // elem points to an array of sz doubles
int sz;
};
Vector::Vector(int s)
:elem{new double[s]}, sz{s} // initialize members
{
}
double& Vector::operator[](int i)
{
return elem[i];
}
int Vector::size()
{
return sz;
}
export int size(const Vector& v)
{ return v.size(); }
- The way we use this module is to import it where we need it.
// file user.cpp:
import Vector; // get Vector's interface
#include <cmath> // get the standard-library math function interface including sqrt()
double sqrt_sum(Vector& v)
{
double sum = 0;
for (int i=0; i!=v.size(); ++i)
sum+=std::sqrt(v[i]); // sum of square roots
return sum;
}
- difference between header and module
+ module is compiled once
+ two modules can be imported in either order
+ if you import something into a module, users of your module do not implicitly gain access to what you imported. import is not transitive
- namespace
namespace My_code
{
class complex { // ... };
complex sqrt(complex);
// ...
int main();
}
int My_code::main()
{
complex z {1,2};
auto z2 = sqrt(z);
std::cout << '{' << z2.real() << ',' << z2.imag() << "}\n";
// ...
}
int main()
{
return My_code::main();
}
bring the name into a scope with a using-declaration:
using std::swap; // use the standard-library swap // ...
swap(x,y); // std::swap()
other::swap(x,y); // some other swap()
- error handling
double& Vector::operator[](int i)
{
if (i<0 || size()<=i)
throw out_of_range{"Vector::operator[]"};
return elem[i];
}
// ...
try{
// exceptions here are handled by the handler defined below v[v.size()] = 7;
// try to access beyond the end of v
}
catch (out_of_range& err) {
// oops: out_of_range error
// ... handle range error ...
cerr << err.what() << '\n';
throw; // rethrow
}
A function that should never throw an exception can be declared noexcept.
- invariants, If operator new can’t find memory to allocate, it throws a std::bad_alloc.
- error handling alternative
+ throwing an exception
error is rare
cannot be handle by the caller
no suitable return path for errors
The error has to be transmitted up a call chain to an “ultimate caller.”
The recovery from errors depends on the results of several function calls
+ return a value indicate failure
failure is normal and expected
an immediate caller can handle the failure
+ terminate the program
cannot recover
- contracts, The standard library offers the debug macro, assert(), to assert that a condition must hold at run time.
assert(p!=nullptr); // p must not be the nullptr
- static assertions, perform simple checks on most properties that are known at compile time
static_assert(4<=sizeof(int), "integers are too small"); // check integer size
- function arguments and return values
concerns are:
Is an object copied or shared?
If an object is shared, is it mutable?
Is an object moved, leaving an “empty object” behind (§5.2.2)?
The default behavior for both argument passing and value return is “copy”
- argument passing
void test(vector<int> v, vector<int>& rv)
// v is passed by value; rv is passed by reference
{
v[1] = 99; // modify v (a local variable)
rv[2] = 66; // modify whatever rv refers to
}
+ move, we give Matrix a move constructor (§5.2.2) and very cheaply move the Matrix out of operator+()
- returning large objects by returning a pointer to it is common in older code and a major source of hard-to-find errors.
- deduce return type
auto mul(int i, double d) { return i*d; } // here, "auto" means "deduce the return type"
- structured binding
struct Entry
{
string name;
int value;
};
Entry read_entry(istream& is) // naive read function (for a better version, see §10.5)
{
string s;
int i;
is >> s >> i;
return {s,i};
}
//we can decorate auto with const and &. For example:
auto [n,v] = read_entry(is);
cout << "{ " << n << "," << v << " }\n";
- Avoid non-inline function definitions in headers;
- Don’t put a using-directive in a header file;
- Don’t overuse return-type deduction;
# Class
- three important kinds of classes
+ concrete classes
+ abstract classes
+ classes in class hierarchies
- concrete types, just like built-in types, Similarly, a vector and a string are much like built-in arrays
- classical user-defined arithmetic type
class complex
{
double re, im; // representation: two doubles
public:
complex(double r, double i) :re{r}, im{i} {} // construct complex from two scalars
complex(double r) :re{r}, im{0} {} // construct complex from one scalar
complex() :re{0}, im{0} {} // default complex: {0,0}
double real() const { return re; }
void real(double d) { re=d; }
double imag() const { return im; }
void imag(double d) { im=d; }
complex& operator+=(complex z)
{
re+=z.re; // add to re and im
im+=z.im;
return *this; // and return the result
}
complex& operator−=(complex z)
{
re−=z.re;
im−=z.im;
return *this;
}
}
complex& operator*=(complex);
// defined out-of-class somewhere
complex& operator/=(complex);
// defined out-of-class somewhere
}
but a non-const member function can only be invoked for non-const objects.
c!=b means operator!=(c,b) and 1/a means operator/(complex{1},a)
- destructor, Plain delete deletes an individual object, delete[] deletes an array.
- initializing containers
class Vector
{
public:
Vector(std::initializer_list<double>);
// initialize with a list of doubles
// ... void push_back(double);
// add element at end, increasing the size by one
// ...
};
{1,2,3,4}, the compiler will create an object of type initializer_list to give to the program
Vector v1 = {1,2,3,4,5}; // v1 has 5 elements
- convert
const_cast for “casting away const.”
A static_cast does not check the value it is converting
reinterpret_cast for treating an object as simply a sequence of bytes
- abstract types
class Container {
public:
virtual double& operator[](int) = 0; // pure virtual function
virtual int size() const = 0; // const member function (§4.2.1)
virtual ~Container() {} // destructor (§4.2.2)
};
- A class that provides the interface to a variety of other classes is often called a polymorphic type
- inherit
class Vector_container : public Container {
int size() const override { return v.size(); }
}