《C++ Primer (5th Edition)》笔记-Part I . The Basics

注:本文以《C++ Primer(英文版)》(5th Edition)为参考。

总共由四部分组成:

《C++ Primer (5th Edition)》笔记-Part I. The Basics 
《C++ Primer (5th Edition)》笔记-Part II. The C++ Library
《C++ Primer (5th Edition)》笔记-Part III. Tools For Class Authors
《C++ Primer (5th Edition)》笔记-Part IV. Advanced Topics



Chapter 1.  Getting Started

略。。。

Part I. The Basics

Chapter 2.  Variables and Basic Types

2.1 Arithmetic types分为两类:integral types & floating-point types.

2.2 新增Arithmetic types:long long, char16_t, char32_t,注意各自的位数限制。

2.3 一般integral types都有signed和unsigned之分,除了两个例外:bool 和 char。标准未规定signed类型的内部表示方式和范围,只要求signed和unsigned范围要对称。

2.4 将无法表示的值赋给unsigned类型,要做取模处理,如将-1赋给unsigned char,要模上256,结果为255。将无法表示的值赋给signed类型则是undefined。

2.5 若一个expression中同时涉及同种类型的signed和unsigned变量,那么将signed变量转化为unsigned类型,转换方式为“wraps around"。这种情况应该尽量避免。不同类型的signed和unsigned变量的处理方式,见稍后分析。

2.6 默认十进制literal是signed类型,八进制、十六进制的Literal则可以为signed或者unsigned。十进制literal取以下最先匹配的类型:int, long, long long。使用超过最大类型范围的literal是错误的用法(测试后,编译没问题,但会有警告信息)。默认floating-point literals的类型是double。默认string literal的类型的是以‘\0'结尾的array of constant chars。

2.7 literal加前缀(对于字符串和字符)或后缀(对于Arithmetic类型)来限定literal的类型。字符(串):u、U、L、u8。integer类型:U(u)、L(l)、LL(ll)。浮点型:F(f)、L(l)。

2.8 整型的四种初始化方式:int a=0, b(0), c{0}, d={0}。后两种叫做“list initialization"。list initialization的值不允许精度丢失。

2.9 Default Initialization的定义。

2.10 声明和定义的区别。int j;//定义,extern int j;//声明, extern int j = 32;//定义。所有Variables只能被定义一次,但可以声明多次。

2.11 我们定义的标识符不应该:1)以两个连续的下划线开头,2)以下划线+大写字母开头,3)在function外面定义,并以下划线开头。

2.12 NULL是preprocessor variable,应尽量用literal nullptr 代替。

2.13 static、const限定的object是Local to file的。const object可以通过在前面添加extern,来变为全局可见。

//test1.cpp
static int test1 = 1;
extern const int test2 = 2;
extern const int test3 = 3;
//test2.cpp
static int test1 = 11;//ok
//extern const int test2
const int test2 = 12;//ok,但如果释放掉上面一行,就会出现重定义错误
extern const int test3 = 13;//not ok! redefined

2.14 const reference的特殊之处:double a = 23; int &b = a;//对于const reference而言,这是正确的用法。

2.15 pointer to const: const int *p; int const *p。const pointer:int *const p。

2.16 top-level const 和 low-level const。

2.17 constant expression(常量表达式)的定义。literal是常量表达式,通过常量表达式初始化的const object也是常量表达式。声明为constexpr 的变量,隐含为const类型,并且被常量表达式初始化。

2.18 “literal types”的定义:能够使用constexpr修饰的类型。包括:arithmetic、reference、pointer types。constexpr pointer可以被literal nullptr、0和有固定地址的对象的地址初始。function内部定义的变量没有固定地址,反之则又; 所以,定义在function外部的object的地址也是常量表达式。

2.19 const int *p 和 constexpr int *p的区别。constexpr const int *p 的意义(P67)。

2.20 定义类型别名:typedef char* pi;using pi = char *。注意const pi pstr和 const char *pstr 的区别。

2.21 type specifier:auto。auto tells the compiler to deduce the type from the initializer,所以,auto类型的variable必须要有一个initializer。

auto 推断出的类型一般过滤掉了top-level const 和 引用(&)。

1) auto i = 0, *p = &i;
2) int i = 0;
const int ci = i, &cr = ci;
auto b = ci; // b is an int
auto c = cr; // c is an int
auto d = &ci; // d is const int *
如果不想过滤掉,需要在auto前加const、在auto后加&。

2.22 type specifier: decltype。the compiler analyzes the expression to determine its type but does not evaluate the expression。decltype 能够handle top-level const 和 reference。当decltype的操作数不是variable时,如果操作数expression返回的是左值,那么decltype返回是该类型的引用类型,否则与实际类型一致。当decltype的操作数是variable时,(虽然变量肯定是返回左值的expression,见Part III, 13.15)只有变量是引用类型时,decltype(variable)才会返回引用类型,否则返回值类型;通常用decltype((variable))的形式来得到引用类型。

2.23 struct A{/*...*/} a1, *a2;等价于struct A{/*...*/}; A a1, *a2;后者更好。C++11标准对类内的数据成员,允许in-class initializer初始化。

Chapter 3. String,Vector,and Array

3.1 using declaration。using std::string。在头文件中不应该包含using declaration。

3.2 string是一种type。注意copy initialization 和 direct initialization的区别。

3.3 string operations。getline(is, s);c++ 11 引入了shrink_to_fit()成员函数。对于静态成员变量npos,其定义为:static const size_type npos = -1;由于size_type为unsigned类型,所以npos为size_type类型的最大值。

3.4 range for statement。for(declaration : expression) statement; 在遍历中,declaration中的变量被来自expression中的值初始化。statement常做字符处理,常用的字符处理函数有(定义在cctype头文件中):isalpha(c), isalnum(c), isdigit(c), isspace(c), islower(c), isupper(c), tolower(c), toupper(c)。

3.5 string的下标为string::size_type类型,值必须>=0&&

3.6 vector是一种class template。能够hold objects of most any type.但是由于reference不是object,所以我vector不能保存引用类型的数据。C++ 11引入了shrink_to_fit()、emplace()、emplace_back()成员函数。

3.7 Value Initialization。注意与2.9 Default Initialization的区别。

3.8 List initializer的倾向性。

vector v{10, 2};//v有两个元素10和2。
vectorv2{10, "hi"};//v2有10个元素。

3.9 对于iterator,C++11为容器引入了cbegin()和cend()成员函数(相应的也有crbegin()、crend())。

3.10 同vector一样,数组也不能保存引用类型。数组的维度值都必须大于0,但是动态数组的维度可以为0 (P478)。

经GCC4.8.1测试, 静态数组维度为0也是可以的,但不能为负值,否则编译错误;另外,于与动态数组,负值也都是可以的。

经VS2012测试,静态数组维度若为零,会有编译错误;另外对于动态数组,维度若为负值,也会有编译错误(提示数组大小不能超过0x7fffffff个字节,所以若为char数组就不会有错误)。

3.11 数组的list initialization。list中的元素个数不能大于维度值。否则,对于静态数组,会有编译错误;对于动态数组,会抛出bad_array_new_length异常:
int array1[3]={1,2,3,4}; int *array2[3]{1,2,3,4};
写了维度,但没有初始化,那数组元素被default initialized;写了维度,用initializer_list初始化,但是initializer_list元素个数小于维度,剩下的元素被value initialized(为0)。
数组直接初始时可以不用写维度(仅对第一维适用,多维数组比较特殊):int arr[] = {1,2,3};这里arr就是有个3个元素的数组,不是指针。同样char str[] = {'a','b','c'}; char str2[] = 'abc'也是数组,但前者有3个元素,后者有4个元素,隐含添加了‘\0'。

3.12 变量类型匹配,中括号的优先级大于*号;int *ptrs[10];  ptrs是一个数组。int (*ptrs)[10];ptrs是一个指针。

3.13 C++ Primer中说道数组的下标为size_t类型(P116),但是也说道the index of the built-in subscript operator is not an unsigned type(P121).这两者的说法似乎是矛盾的。

在VS2012中对静态数组和动态数组均作了测试,下标为负值的时候,是作为负值来处理的,没有转化为unsigned类型。

	int array[2][3] = {1,2,3, 4, 5, 6};
	int (&array2)[3] = array[1];
	cout << array2[-1] << endl;//输出3
	int *pa = array2 + 1;
	cout << pa[-1] << endl;//输出4

3.14 C++11标准引入了begin(), end()两个库函数。可以用在数组(或者其他容器)上,但不可以用在指针(动态数组)上。

3.15 指针间的差值类型为ptrdiff_t。另,iterater间的差值类型为difference_type。

3.16 C串常用函数:strlen(p); strcmp(p1, p2); strcat(p1, p2); strcpy(p1, p2)。后两个均是从p2转到p1,所以应该保证p1有足够的空间。

3.17 C串一般写成字符数组的形式,也可以写成pointer to const的形式,const char *str = "abc"。这里const尽量带上(虽然不带也不会编译错误,但毕竟是从const char[]赋值过来的)。若同时定义const char *str2 = "abc"。那么str和str1的值是一样的,即str == str2。这里的“abc”是放在文字常量(C++的存储区还有全局静态区、堆区、栈区、程序代码区)的,只存了一份。

3.18 注意多维数组的range for写法,以及数组类型的别名定义。

Chapter 4 Expressions

4.1 左值、右值说明(P135)。

4.2 优先级只指定了how the operands are grouped,but it says nothing about the order in which the operands are evaluated.

int a = f1()*f2()。这里f1 和 f2的调用顺序是不确定的。

所以在一个expression中同时引用和修改同一个变量的做法是错误的。

cout << i << " " << ++i << endl; //Undefined

另外,有四种操作符是保证操作数的计算(evaluated)顺序的:“&&”,“||”, “?:”,“ ,”。

4.3 除法算术运算中,除数和被除数同号,则商为正,否则商为负。以前的C++标准中,负的商,是向上和向下四舍五入的;但在C++11标准中,要求商总是向0四舍五入。

4.5 取摸m%n的算术运算中,以前的标准:余数的符号与n一致;已经被废弃。在C++ 11标准中,余数的符号始终与m保持一致。所以在现在的标准下:

(-m)/n = m/(-n) = -(m/n);

m%(-n) =  m % n;

(-m)%n = - (m%n);

4.6 在使用list initialization时,不管左操作数为何种类型,只要initializer list为空,那么左操作数都会被“Value-initialized”(P145)

4.7 尽量使用prefix increment。

4.8 位操作。在做位操作时,“small integer”会自动被promoted为“larger integral type”。当操作数为负数时,符号位是如何处理的is machine dependent. 但是,doing a left shift that changes the value of the sign bit is undefined.(P153)。不是太明白具体意思。

4.9 移位操作符的右操作数必须不能为负,且其值必须小于结果类型中的位数,否则操作是undefined。左移操作,就是在在右边补零;右移操作,若左操作数为unsigned类型,那就左边补零,若左操作数为signed类型,那么结果是implementation defined(拷贝符号位,或者补零)。

4.10 sizeof结果是size_t类型的const expression。sizeof并不evaluate其操作数。在C++11标准下,sizeof能够用scope操作符去询问类内成员的size。

4.11 进行隐式类型转换的情况:

The compiler automatically converts operands in the following circumstances:
•In most expressions, values of integral types smaller than int are first promoted to an appropriate larger integral type.
•In conditions, nonbool expressions are converted to bool.
•In initializations, the initializer is converted to the type of the variable; in assignments, the right-hand operand is converted to the type of the left-hand.
•In arithmetic and relational expressions with operands of mixed types, the types are converted to a common type.
•As we’ll see in Chapter 6, conversions also happen during function calls.

4.12 Integral Promotions:

bool,char,unsigned char, signed char, short 和 unsigned short,当其值 fit in an int时, 被promoted为int,否则被promoted为unsigned int。

larger char type(wchar_t, char16_t, char_32_t)则被提升为int, unsigned int, long, unsigned long, long long, unsigned long long中能够fit的最小的类型。

4.13 如果一个操作符有不同类型的操作数时,当unsigned操作数is same or larger than signed操作数, 那么signed操作数被自动转化为unsigned类型;当signed操作数has a larger type than unsigned 操作数时,结果将是machine dependent(如果所有的unsigned类型的值能够fit in the larger type,那么unsigned将会被转化为signed类型,否则,signed操作数将被转化为unsigned类型)

4.14 显式转换:static_cast, dynamic_cast, const_cast, reinterpret_cast。其中reinterpret_cast is inherently machine dependent。

4.15 操作符优先级关系表。

《C++ Primer (5th Edition)》笔记-Part I . The Basics_第1张图片《C++ Primer (5th Edition)》笔记-Part I . The Basics_第2张图片

Chapter 5 Statement

5.1 在switch语句内部,只能最后一个case标号或者default标号后面定义变量,否则会编译出错。或者,可以在大括号内部定义变量。

5.2 library exception class都定义了what成员函数,返回C串。如果异常没有被捕获,将会调用库函数terminate,这个函数的定义是system dependent的,但是他保证程序会停止更近一步的执行。

5.3 我们只能default initialize exception, bad_alloc 和 bad_cast对象。而对于其他异常类型,则正好相反,可以用C串或者String初始化,但是不能够default initialize 。

Chapter 6 Function

6.1 A function is a block of code with a name.

6.2 函数的返回类型不能为数组或者函数,但可以是其类型的指针。形参和函数内定义的变量叫做Local variables。

6.3 top-level const on parameters are ignored.

void fcn(const int i){/*...*/}
void fcn(int i){/*...*/} //error:redefines fcn(int)

6.5 数组作为参数,会自动将数组转换为指针,但是此转换仅对一层操作。对多维数组,如 f(int array[][]),第一层可以不用写维数,因为本来就会自动转指针的,但是指针指向了没有维数的数组,这种做法是错误的,所以正确的写法应该是f(int array[][10]),等价于f( int (*array)[10] )。

6.4 对于参数个数不定的函数,可以使用C中方式,用ellipsis代替省略的参数(只能作为参数列表中的最后一个参数)。C++11引入的新的不定参数的支持。1)如果所有实参的类型都一样,可以传入一个library type named initializer_list;2)如果实参的类型可变,可以将函数写为variadic template形式。

6.6 initializer_list is a library type that represents an array of values of the  specified type.

This type is used to access the values in a C++ initialization list, which is a list of elements of type const T.

auto il = { 10, 20, 30 };  // the type of il is an initializer_list 

6.7 initializer_list的成员函数很有限(size(),begin(),end()),并且其拷贝构造函数或者赋值操作符并不会copy列表中的元素,而是共享原列表中的元素。即若lst2(lst); lst2=lst; 那么lst2和lst共享数据,由于数据时const类型,所以均不可修改。另外,initializer_list没有定义下标操作符,所以只能通过其iterator(实际为指针)+相应偏移来访问其中的元素。

6.8 Reference Returns are Lvalues! 如果返回类型为一个class的值类型,即使能够调用非const成员函数,但是其任然是右值,可以通过decltype来验证。

6.9 除了main函数,其他任何分为类型为非void的函数,必须得返回一个value。

6.10 注意int (*func(int i))[10]的解析方式。

6.11 C++11引入Trailing return type。

6.12 重载函数,必须在参数的数目或者类型上有区别,如果仅在返回类型上不同,不能算作重载,而且会有编译错误。(注意6.3,top-level const is ignored)。

6.13 Names do not overload across scopes。Name lookup happens before type checking.

string read();
void print(const string &);
void print(double); // overloads the print function
void fooBar(int ival)
{
	bool read = false; // new scope: hides the outer declaration of read 
	string s = read(); // error: read is a bool variable, not a function 
	// bad practice: usually it's a bad idea to declare functions at local scope
	void print(int); // new scope: hides previous instances of print 
	print("Value: "); // error: print(const string &) is hidden 
	print(ival); // ok: print(int) is visible
	print(3.14); // ok: calls print(int); print(double) is hidden
}

6.14 默认参数仅用在函数声明处,函数定义时参数不能有默认值。声明时,一个参数若有默认值,那么其后的每个参数也都必须有默认值。(在同一Scope内)声明同一个函数多次是合法的。但是,在一个scope内,每一参数只能有一次默认值指定;在后续的声明中,可以对没有指定默认值的参数指定默认值(但其右面的所有参数必须已经指定了默认值):

string screen(sz, sz, char = ' ');
string screen(sz, sz, char = '*'); // error: redeclaration
string screen(sz = 24, sz = 80, char); // ok: adds default

6.15 参数的默认值不能用local variable来指定,除此之外,可以用任意的,能够返回兼容类型的expression来指定。

6.16 inline是request不是命令。对于复杂的函数(如类的构造、析构函数),编译器通常会生成相应的outline副本;另外,对inline函数的调用方式也会影响函数是否被inlined,对于一个简短的函数,编译通常愿意inlining它,正常调用的也是inlined的函数,但是如果通过对inline函数取地址,然后通过函数指针调用时,通常就不被编译器inlined了。

inline void f(){/**/}	//假设编译器有意愿inline“对f的调用”
void (*pf)() = f;		//pf指向f
f();				  	//这个调用将被inlined,因为他是正常调用
pf();					//这个调用通常不被inlined,因为他是通过函数指针完成
6.17 C++11引入的constexpr也可以用在函数上。constrexpr function隐含是inline function。

6.18 constrexpr function的函数体中除了return语句,其他语句都不能产生任何actions,即只能包含null statements、type aliased 和 using declarations。

6.19 constrexpr function是允许返回一个非constexpr的值的,这种情况通常发生在传入的参数非constexpr时。

6.20 assert是一个preprocessor macro。asser的行为依赖于preprocessor variable named NDEBUG。如果NDEBUG被定义了,那么assert将什么都不会做。默认NDEBUG是不被定义的。由于assert是在run-time时做判断,无法满足模板的编译器检查的需求,所以C++11引入了static_assert( constant-expression, error-message );来做编译期检查(C++ Primer未提及)。

template< class T >
struct Check{
	static_assert( sizeof(int) <= sizeof(T), "T is not big enough!" ) ;
};

6.21 compiler为每个函数定义了__func__(local static array of const char)来保存每个函数的名字。另外,preprocessor定义了四种在debug时常用的names:__FILE__, __LINE__, __TIME__, __DATA__。

6.22 Function Matching步骤:1)需找candidate functions(相同名字,可见),2)挑选viable functions(相同数目的参数,并且对应参数必须可以匹配或者be convertible to),3)Finding the best match, if any。overall best match的定义:有且只有一个函数满足:a.对于每个参数的匹配,都不比其他viable function的匹配差;b. 至少有一个参数的匹配比其他viable function的匹配都好。

6.23 conversions are ranked as follows: 1)exact match:a.形参和实参类型一致,b. 实参从数组、函数转化为其指针形式,c. top-level const的添加和删除;2)conversion to const;3)类型promotion;4)算术或者指针转换;5)class-type conversion。这里没有说明类的derived-to-base转换(Part III, 15.14)有优先级。 另:在Part III中的14.14小项中,有对涉及class-type conversion的详细分析。

6.24 函数指针必须和重载函数中的一个exactly match。函数不能作为另一个函数的返回类型,编译不过,函数指针可以;函数可以做为另一个函数参数类型,但是自动被作为函数指针对待,可能出现重定义的错误。数组也不能做为函数返回类型,作为参数也被作为指针对待,但是函数可以再函数引用的方式作为参数。

6.25 int (*f(int))(int*, int)的解析:1)f有参数列表,所以f是函数;2)f前面有*,所以f返回的是指针;3)这个指针有参数列表,所以指针指向一个函数;4)这个函数的返回类型是int。

Chapter 7 Classes

7.1 The fundamental ideas behind classed are data abstraction encapsulation。数据抽象技术则是通过“接口”与“实现”的分离来实现。

7.2 成员函数遗憾接受一个this指针,非const成员函数,this指针为一个const pointer to nonconst; const成员函数,this指针为const pointer to const。

7.3 C++11引入的“=default”可以写在类内部的声明后面,也可以写在类外的定义处,仅有inlined和非inlined的区别。定义在外面时,不用写函数体(包括大括号)。

7.4 尽管initializer list为空,但是类内的成员变量仍然会在构造函数执行之前被初始化(default initialization)。

7.5 关键字class和struct的区别:1)成员的默认access specifier,前者为private,后者为public;2)默认继承,前者为private,后者为public。

7.6 友元不是类的成员,不受访问权限限制。有元声明不等于函数声明(仅指定了友元的访问权限,不是常规的声明),在使用之前人需要声明。

7.7 类型成员(type member)也是subject to the same access controls as any other member and may be either public or private。

7.8 inline 声明应该写在声明或者定义处,或者两处都写;最好仅写在定义处。

7.9 mutable 成员变量,is never const。mutable不能与const、static同时用。

7.10 每个类都定义了唯一的类型,两个不同的类就定义了两个不同的类型,尽管他们定义了相同的成员。在不同的编译单元(CPP文件)中,结构不一样的两个类可以用同一个名字。

7.11 类的(前向)声明。使用限制:我们只能定义该类型的指针或者引用,另外我们可以声明(不是定义)一个函数,此函数可以用该类型做为参数或返回类型。

7.12 友元函数可以定义在类的内部,这种函数默认是inline的;即使在内部定义,在(成员函数)使用的时候也是需要先声明的; 声明的时候不需要加scope说明(P281)。

7.13 类和非成员数的友元声明时,不需要先声明类和非成员函数。但是成员函数的友元声明前,是需要提前声明的。在Part III中的16.6小项有针对类模板中友元的说明。

7.14 一旦类名字出现,在剩下的定义(参数列表,函数体)都认为是在类的scope内的,返回类型并不在类的scope内,如果用Trailing return type, 就在类的scope内了。

7.15 通常的名字查找规则:1)在同一个block类找,2)如果找不到,在enclosing scope(s)找,3)如果找不到,编译错误。

7.16 类成员函数定义中的名字查找规则:1)本成员函数内,2)类内,3)(定义处的)enclosing scope(s)。另外,多继承时名字查找规则见Part IV,18.17、18.19 。

7.17 名字的解析,从文件中名字出现的位置开始。

7.18 类成员函数的定义中为什么可以使用声明在更后的成员?1)成员声明先被编译,2)整个类可见后,才编译成员函数的函数体。

7.19 类型名的特殊之处:通常尽管在inner scope已经使用了类型名字,我们仍然可以在inner scope内重定义该类型名字。但是在类内,这是不允许的(即使重定义相同的类型)。注意大部分编译器是不检测这种错误的(包括VS2012),选择默默接受,但是这种编程是错误的。

typedef double DB;
void f(){
	DB di = 22.0;
	typedef int DB;//redefine, OK!
	DB ii = 11;
};
typedef double DB;
class A{
	DB di;
	typedef double DB; //error:cannot redefine DB
	DB ii;
};
7.20 构造函数的initializer list仅仅指定了要初始化的value,并没有指定初始化顺序,成员的初始化顺序与其在类内的定义顺序一致。

7.21 in-class initializer只是对initializer list的辅助。类成员的初始化顺序任然是与声明顺序一致,当初始化成语成员i时,若initializer list中有设置,就用其初始化,否则查看i是否有in-class initializer,若有,即用其初始化,否则执行default initialization。

7.22 C++11引入了委托构造函数(delegating constructors):构造函数可以使用其它构造函数。被使用的构造函数必须作为原构造函数的唯一的一个entry。

7.23 隐式类类型转换,explicit,仅能写在类内构造函数的声明出,不能写在类外构造函数的定义处。对于多个参数的构造函数,是否有使用explicit的必要?Primer 认为没有,实际上是有的,加上explicit,可以避免使用list initialization的隐式转化。注意:Only one class-type conversion is allowed!。

7.24 Aggregate Classes:能用大括号直接初始化的类。1)所有数据成员都是public,2)没有定义constructor,3)没有in-class initializer,4)不能有基类或者虚函数。析构函数,普通函数不影响。

7.25 Literal Class。literal type有算术类型、引用类型、指针类型、还有特定的类类型。 数据成员都是literal type的aggregate class都是literal class。如果一个nonaggregate class 满足一下约束,就也是literal class:1)数据成员必须是literal type,2)至少要有一个constexpr constructor,3)如果使用了in-class initializer,对于built-in类型必须是const expression,对于class类型必须用其自身的constexpr constructor。4)必须用默认的析构函数。

7.26 constexpr constructor 可以声明成=default,否则函数体通常是空的。constexpr constructor 必须初始化每一个数据成员(P300)。(这不是与可以声明为=default矛盾吗?)

7.27 类的静态数据成员必须在类外定义(静态成员函数就不用),在类外定义时,可以访问本类的私有静态成员函数。类的静态数据成员一般是不允许使用in-class intitializer的,但对于static const成员可以(还须在类外定义),对static constexpr成员必须用const expression在类内初始化(不能在类外定义)。静态数据成员可以通过类名+”::“的方式来访问,也可以通过对象+”.“的方式来访问。模板类的静态成员说明可以参加PartIII中16.9小项。

class A{
private:
	static int f(){}
public:
	static int sa;//这里不允许做in-class initilization
	static constexpr int sca = 30;
};
int A::sa = f();//静态数据成员的定义,通常写在源文件(*.cpp, *.cc文件)

7.28 静态成员可以使用incomplete type。静态成员可以作为其他成员函数的默认参数。




注:本文以《C++ Primer(英文版)》(5th Edition)为参考。

总共由四部分组成:

《C++ Primer (5th Edition)》笔记-Part I. The Basics 
《C++ Primer (5th Edition)》笔记-Part II. The C++ Library
《C++ Primer (5th Edition)》笔记-Part III. Tools For Class Authors
《C++ Primer (5th Edition)》笔记-Part IV. Advanced Topics




你可能感兴趣的:(C++,★★★★★,笔记)