本文主假如浏览Thinking in C++ 第一卷的一些笔记。主假如一些注重点
Thinking in C++ Chapter 2
Translator:
编译器编译顺序步骤:
函数或许变量的声明与定义
衔接
Thinking in C++ Chapter 3
函数返回申明
平常函数库
关于for轮回的申明
switch申明
申明符(specifier)
void*指针申明
变量的有用作用域
全局变量
静态(static)变量
衔接(linkage品种)
预处置惩罚宏
typedef语法申明
enum申明:
Thinking in C++ Chapter 5
友元
嵌套友元
struct其他
Thinking in C++ Chapter 6
组织函数响应申明
delete void*
默许组织函数
Thinking in C++ Chapter 7
overload
范例平安衔接(type-safe linkage)
Union
默许参数运用划定规矩:
占位符参数
Thinking in C++ Chapter 8
const申明
const指针
赋值和范例搜检
暂时变量
通报和返回地点
规范参数通报
类内数据成员为const初始化
编译时期的常量
const对象和成员函数
Thinking in C++ Chapter 9
内联函数
组织函数和析构函数隐蔽行动
预处置惩罚器
Thinking in C++ Chapter 10
static
static对象
内部链接
others
stactic/const/stctic const
静态成员函数
指针数组/数组指针
函数指针
函数地点(Function Address)
函数指针
挪用函数指针
函数指针数组
typedef简化工作量
namespace
namespace
未命名的名字空间
using directive& using declaration
new/malloc/delete/free
new/malloc
delete/free
重载全局new和delete
类重载new和delete
为数组重载new和delete
定位new/delete
继承与组合(Inheritance&Composition)
初始化表达式
组织函数和析构函数挪用的递次
非自动继承的函数
继承与静态成员函数
私有继承
向上范例转换和拷贝组织函数
运算符重载
+=,-=,*=,/=,=等一类运算符重载
函数参数和返回值申明
Prefix ++ & Postfix ++
常量传值返回与返回值优化
operator[]
operator->(指针间接援用运算符)
operator->*
运算符成员函数基础方针
copy-on-write
自动范例转换
Thinking in C++ Chapter 2
Translator:
诠释器(interpreter)
编译器(complier)
编译器编译顺序步骤:
预处置惩罚器(preprocessor)处置惩罚预处置惩罚指令
编译分为两遍,第一遍剖析预处置惩罚代码生成节点数,在举行第二步之前举行全局优化(global optimizer),第二遍代码生成器(code generator)剖析代码树生成机器语言或许汇编语言
静态范例搜检在第一遍中举行
函数或许变量的声明与定义
void function();//声明void function(){}//定义extern int a;//声明int a;//定义
衔接
编译历程的末了阶段,把编译器生成的目的模块衔接成可实行文件(操纵系统可以辨认)
Thinking in C++ Chapter 3
函数返回申明
return语句退出函数,返回到函数挪用后的那点,遐想栈的操纵
平常函数库
由库治理器来治理对象模块,这个库治理器就是治理.lib/.a文件的
关于for轮回的申明
for(initialization;conditional;step)
for轮回起首实行initialization,其次推断conditional,满足则进入轮回,实行完轮回举行step步骤
switch申明
switch(selector){ case integral-value:statement;break; ... defualt:statement; }
switch中的selector必需为整数值,integral-value必需为整形数值
selector也可以为enum范例值
申明符(specifier)
用于转变基础内建范例的寄义并将基础范例扩大成一个更大的鸠合
1. int: short int / int / long int(运用short和long时,int关键字可以省略)
2. float/double: 没有long float只要long double,即:float / double /long double
3. signed/unsigned:标记位(适用于整形和字符型)
void*指针申明
void*指针可以赋值为任何范例的地点,然则会丧失范例信息,不恰当的范例转换会致使顺序崩溃
变量的有用作用域
从定义点最先,到和定义变量之前最相近的开括号配对的第一个闭括号,也就是说作用域由变量地点的近来一对括号肯定。
全局变量
全局变量的生命周期一直到顺序完毕,可以运用extern关键字来运用另一个文件中的全局变量
静态(static)变量
static变量长处是在函数局限之外它是不可用的,不可以随意马虎转变,使毛病部分化,当运用static于函数名和一切函数外部变量时,它的意义是“在文件的外部不可以运用该名字”,即具有文件作用域如
//file1.cppstatic int fs;int main(){ fs = 1; }//file2.cppextern int fs;//编译器不会找到file1.cpp文件中的fs,即文件作用域void function(){ fs = 100; }
extern static int i;int main(){ cout << i << endl; }static int i = 0; 将涌现error,即全局静态变量声明是有文件作用域的,编译器将会发作毛病
衔接(linkage品种)
内部衔接:只对正在编译的文件穿件存储空间,为每个标识符竖立零丁的存储空间,内部衔接由关键字static指定
外部衔接:对一切编译过的文件竖立一个零丁的存储空间,行将一切变量和函数包含在该空间中
自动(部分)变量只是暂时存在于客栈中,衔接器不晓得自动变量,所以这些变量没有衔接
预处置惩罚宏
#define PRINT(STR,VAR) \ cout << STR << VAR << endl; \ cout << VAR << STR <<endl 即可以像挪用函数一样挪用PRINT,这里预处置惩罚宏分行运用'\',宏只是睁开,并替代 #define PRINT(STR,VAR) \ cout << #STR << VAR << endl 这里'#'示意STR字符串化(stringizing),比方:int i = 0;PRINT(i,i); //这里输出应当是 i:0
typedef语法申明
typedef existing-type-description alias-name
typedef unsinged long ulong;
typedef int* intPtr
typedef struct MyStruct{
//这里是C常营的构造定义
} MyStruct;typedef int (*fun)(int,int) //函数指针别号为fun
enum申明:
c++中对enum的搜检更加严厉,c中许可a++(a为color型罗列),然则c++中不许可,由于a++做了两次转换,起首将color范例转换为int,然后自增1以后,将该值在转换成color,第二次转换时不法的
Thinking in C++ Chapter 5
友元
运用friend关键字可以接见内部私有成员变量或许成员函数
struct X;struct Y{ void f(X*); };struct X{ private: int i; public: void initialize(); friend void g(X*,int); //Global friend friend void Y::f(X*); //struct member friend friend struct z; //Entire struct is a friend friend void h(); }
Y::f(X*)援用了一个X对象的地点,编译器晓得怎样通报一个地点,不论被通报的是什么对象,地点具有牢固大小,当试图通报悉数对象时,编译器必需晓得X的悉数定义以肯定它的大小以及怎样通报,运用不完全范例申明(incomplete type specification),即在struct Y之前声明struct X;
嵌套友元
嵌套构造不能自动取得接见private成员权限,可以运用以下要领接见
声明嵌套构造
声明该构造是全局局限内运用的一个friend
定义该构造
const in sz = 20;struct Holder{ private: int a[sz]; public: void initialize(); struct Pointer; friend Pointer; struct Pointer{ private: Holder* h; int* p; public: void initialize(Holder* h); void next(); void previous(); void top(); void end(); int read(); void set(int i); }; };void Holder::initialize(){ memset(a,0,sz*sizeof(int)); }void Holder::Pointer::initialize(Holder* rv){ h = rv; p = rv->a; } ...int main(){ Hodler h; Holder::Pointer hp; int i; h.initialize(); hp.initialize(&h); ... }
struct其他
//: Hadler.h class Handler{ struct Cheshire; Cheshire* smile; public: ...}; //:~ //:Handler.cpp struct Handler::Cheshire{ int i; ...}...
Handler.h文件中struct Cheshire是一个不完全的范例申明或许类声明,细致类定义放在了完成文件中
Thinking in C++ Chapter 6
组织函数响应申明
class Object{public: Object(int number = 0);private: int m_number; };//:~//: mainint main(int argc, char *argv[]) { int i = 0; switch(i){ case 0: Object obj1{1}; break; case 1: //error: cannot jump from switch statement to this case label "case 1:" Object obj2{2}; //jump bypasses variable initialization "Object obj1{1};" break; } return 0; }
上述代码报错
switch回跳过组织函数的的序列点,以至组织函数没有被挪用时,这个对象也会在背面的 顺序块中顺序块中起作用,这里发作毛病
是确保对象在发作的同时被初始化。goto也会发作如许的毛病。
delete void*
当void*指向一个非内建范例的对象时,只会开释内存,不会实行析构函数
默许组织函数
class Object{public: Object(int number);private: int m_number; };Object object[2] = {Object{1}};
Object没有默许组织函数,数组声明初始化时将报错,object[1]必需有默许组织函数举行初始化,不然报错当且仅当没有组织函数时编译器会自动竖立一个默许组织函数
Thinking in C++ Chapter 7
overload
运用局限和参数可以举行重载
void f();class X{void f();};
范例平安衔接(type-safe linkage)
1.cppvoid functin(int);2.cppvoid function(char);int main(){ function(1); //cause a linker error; return 0; }
编译胜利,在C中衔接胜利,然则在C++中衔接失足,这是C++中的一种机制:范例平安衔接
Union
class SuperVar{ enum{ character, integer, floating_point } vartype; union{ char c; int i; float f; }; public: SuperVal(char ch); SuperVal(int ii); SuperVal(float ff); void print(); }; SuperVal::SuperVali(char ch){ vartype = character; c = ch; } SuperVal::SuperVali(int ii){ vartype = integer; i = ii; } SuperVal::SuperVali(float ff){ vartype = floating_type; f = ff; }void SuperVal::print(){ switch(vartype){ case character: cout << "character:" << c <<endl; break; case integer: cout << "integer :" << i <<endl; break; case floating_point: cout << "float :" << f <<endl; break; } }int main(){ SuperVar A('c'),B(12),C(1.44f); A.print(); B.print(); C.print(); return 0; }
enum没有范例名,由于背面没有必要触及美剧的范例称号,所以罗列范例名可选,非必需
union没有范例名和标识符,称为匿名团结(anonymous union),不须要运用标识符和以点操纵符体式格局接见这个union的元素
接见一个匿名团结成员就像接见平常变量一样,唯一区分在于:该团结的两个变量占用统一内存空间,假如匿名union在文件作用域内(在一切函数和类之外),则它必需声明为static,以使它有内部的衔接
默许参数运用划定规矩:
只要参数列表的后部参数才够是默许的
一旦在一个函数挪用中最先运用默许参数,那末这个参数背面的一切参数都必需为默许的
占位符参数
void f(int i, int = 0, float = 1.1); //version 1void f(int i ,int , float flt); // version 2
个中version 2除了i,flt之外中心参数就是占位符参数
Thinking in C++ Chapter 8
const申明
C++中const默许为内部衔接,仅在const被定义的文件中才可见,在衔接时不能被别的编译单元瞥见,当定义一个const时必需赋值给它,除非运用extern举行申明
extern const int bufsize;
平常c++不为const竖立空间,将其定义保存在标记表内,然则上面的extern举行了强迫内存空间分派,别的如取const的地点也是须要存储空间的分派。
关于庞杂的构造,编译器竖立存储,阻挠常量折叠。在C中const默许为外部衔接,C++默许为内部衔接.涌现在一切函数外部的const作用域是悉数文件,默许为内部衔接
const指针
const润饰指针正指向的对象 const int* a;
const润饰在指针中的地点 int* const a;
赋值和范例搜检
const对象地点不可以赋值给一个非const指针,然则可以吧一个非const对象地点赋值给一个const指针
字符数据的字面值:
char * cp = "howdy";char cp[] = "howdy";
指针cp指向一个常量值,即常量字符数组,数组cp的写法许可对howdy举行修正
暂时变量
在求表达式值时期,编译器必需竖立零时变量,编译器为一切的暂时变量自动生成为const
通报和返回地点
void t(int*) {}void u(const int* clip){ //*clip = 2; error int i = *clip; //int * ip2 = clip error;}const char* v(){ return "result of functin 0"; }const int * const w(){ static int i; return &i; }int main(){ int x= 0; int * ip = &x; const int * cip = &x; t(ip); //ok //t(cip); not ok; u(ip); //ok u(cip);//ok //char * cp = v(); not ok const char* ccp = v();//ok //int * ip2 = w(); not ok const int * const ccip = w();// ok const int* cip2 = w();//ok //*w() = 1; not ok}
const指针不可以赋值给非const指针,然则非const指针可以赋值给const指针
函数v()返回一个从字符数组的字面值中竖立的const char *,在编译器竖立了它并把它存储在静态存储区以后,该声明实际上发作该字符数组的字面值的地点
函数w()返回值请求这个指针以及这个指针所指向的对象均为常量,与函数v()相似,由于i是静态的,所以函数返回后返回值依然有用
const int* const w()只要在作左值时第二个const才展现作用,所以w()返回值可以赋值给const int *
规范参数通报
可以将暂时对象通报给const援用,但不能将一个暂时对象通报给吸收指针的函数,关于指针必需明白接收地点(暂时变量老是const)
class X {public: X() {} }; X f(){return X();}void g1(X&){ }void g2(const X&){ }int main(int argc, char *argv[]) { g1(f()); //error! g2(f()); return 0; }
类内数据成员为const初始化
必需在组织函数的初始化列表中举行初始化
编译时期的常量
一个内建范例的static const可以算作编译时期的常量,然则该static const必需在定义的处所举行初始化
无标记enum也可以算作为编译时期常量,一个罗列在编译时期必需有值
class X{ enum {size = 1000}; //same as static const static const int size = 1000; int i[size]; }
const对象和成员函数
若将一个成员函数声明为const,则该成员函数可以被const对象挪用
const成员函数可以挪用非const成员和const成员,非const成员函数一样可以运用const成员
const对象只能挪用const成员函数,非const对象挪用非const成员函数
Thinking in C++ Chapter 9
内联函数
内联函数与平常函数一样实行,然则内联函数在恰当的处所像宏一样睁开,不须要函数挪用的开支(压栈,出栈),任安在类内部定义的函数自动成为内联函数
内联函数体过大时,编译器将摒弃运用内联
当取函数地点时,编译器也将摒弃内联
一个内联函数在类中向前援用一个还没有声明的函数时,是可以的,由于C++划定只要在类声明完毕后,个中的内联函数才会被盘算
class Forward{ int i; public: Forward():i(0){} int f() const {return g()+i;} int g() const {return i;} }
组织函数和析构函数隐蔽行动
class X { int i,j,k;public: X(int x = 0):i(x),j(x),k(x) { cout << "X" <<endl;} //X(int x):i(x),j(x),k(x){cout << "X" <<endl;} ~X(){cout << "~X" <<endl;} };class Y{ X q,r,s; int i;public: Y(int ii):i(ii){cout << "Y" <<endl;} ~Y(){ cout << "~Y" <<endl; } };int main(int argc, char *argv[]) { Y y(1); return 0; }//:~//:outputX X X Y ~Y ~X ~X ~X
类中包含子对象,组织函数先挪用子对象的组织函数,假如没有默许组织函数,则必需在初始化列表中举行初始化,然后在挪用类的组织函数
类中包含子对象,先挪用类的析构函数在挪用子类的析构函数
预处置惩罚器
1. #define DEBUG(x) cout << #x "=" << x<<endl;#:字符串化,详见REF2. #define FIELD(a) char* a##_string;int a##_size##:标志粘贴,许可设两个标识符并将它们粘贴在一起发作新的标识符
Thinking in C++ Chapter 10
static
在牢固地点上举行存储分派,在一个特别的静态数据区上竖立,不是在客栈上发作
对一个特定编译单元来讲是部分的,static可以掌握名字的可见性,该名字在这个单元或许类之外是不可见的
static对象
假如没有为一个内建范例的静态变量供应一个初始化值,编译器会确保在顺序最先时它被初始化为零(转化成恰当的范例)
假如在定义一个静态对象时没有指定组织参数时,该类必需有默许的组织函数
内部链接
常量、内联函数默许状况下为内部链接
others
一切全局对象隐含为静态存储
对static函数意味着只在本单元可见,成为文件静态(file static)
stactic/const/stctic const
static成员必需在类外初始化,假如不初始化,则编译器不会举行默许初始化,关于非内建范例,可以运用组织函数初始化替代“=”操纵符
类内const成员必需在组织函数的初始化列表中举行初始化
static const变量(内建范例)必需在声明的处所就初始化
static对象数组,包含const和非const数组必需在类外部初始化
自定义class类声明为stctic,不论其为const或许非const都必需在类外初始化
类的静态成员必需举行初始化后才够运用
静态成员函数
类的静态成员函数不能接见平常数据成员或许函数,只能接见静态数据成员,也能挪用其他静态成员函数
静态成员函数没有this指针
指针数组/数组指针
优先级低的先读
*p[] 指针数组
(*p)[] 数组指针,即指向一个数组
函数指针
函数地点(Function Address)
函数的地点:函数名后不跟参数
void fun(){}fun即为函数地点fun()为函数的挪用
函数指针
double pam(int); //prototypedouble (*pf)(int); //function pointerpf=pam;//pf now points to the pam();
挪用函数指针
double x=pf(5);double x=(*pf)(5);
函数指针数组
const double* f1(const double ar[],int n);const double* f2(const double [],int);const double* f3(coanst double *,int);//f1,f2,f3函数声明实质一样const double* (*pa[3])(const double * , int) = {f1,f2,f3}; //[]优先级高于*,所以示意pa是个数组,数组中包含三个指针auto pb = pa;const double * px = pa[0](av,3);const double * py = (*pb[1])(av,3);double x = *pa[0](av,3);double y = *(pb[1])(av,3);
指向悉数数组的指针,即是一个指针,而不是一个数组,优先级低的先读
*p[] 指针数组
(*p)[] 数组指针,即指向一个数组
const double* (*(*pd)[3])(const double* , int) = &pa;->等价情势 auto pd = &pa;
pd指向数组,*pd就是数组,而(*pd)[i]是数组中的元素,即函数指针
函数挪用:
(*pd)[i](av,3)->此处返回const double * *(*pd)[i](av,3)->此处返回 double 别的一种函数挪用略庞杂(*(*pd)[i])(av,3)->返回const double * *(*(*pd)[i])(av,3)->返回 double
typedef简化工作量
typedef const double* (*p_fun)(const double* ,int); p_fun p1 = f1; p_fun pa[3] = {f1,f2,f3}; p_fun (*pd)[3] = &pa;
namespace
namespace
namespace只能在全局局限内定义,然则可以互相嵌套
在namespace定义的末端,右花括号背面没必要跟一个分号
可以按类的语法来定义一个namespace,定义的内容可以在多个头文件中连续,就好像反复定义这个namespace
//:header1.h namespace MyLib{ extern int x; void f(); //...} //:~ //:header2.h namespace MyLib{ extern int y; void g(); //...}
一个namespace的名字可以用另一个名字来作为它的别号
namespace lib = MyLib;不能像类一样竖立一个名字空间的实例
未命名的名字空间
namespace { class A{}; class B{}; int i,j,k; //...}
将部分名字放在一个未命名的名字空间中,不须要加上static就可以作为内部衔接
using directive& using declaration
using directive:using namespace xxusing declaration:using xx::f;
new/malloc/delete/free
new/malloc
new盘算内存大小,并挪用组织函数,malloc须要手工盘算内存大小,不挪用组织函数
delete/free
delete先实行析构函数,在清空内存,free直接清空内存
delete用于void*时将不会挪用析构函数,直接清空内存
重载全局new和delete
重载的new必需有一个size_t参数,该参数由编译器发作并通报给我们,分派内存的长度,必需返回一个指向即是该长度的对象的指针,假如没有找到存储单元,则返回一个0,但是假如找不到存储单元,不能仅仅返回0,还应当有new-handler或发作一个非常信息
new返回void*,而不是指向任何特定范例的指针,只须要完成内存分派,而不是完成对象的竖立,直到组织函数挪用才完成对象的竖立,挪用组织函数是编译器完成的
delete参数是由new分派的void*指针,该参数是在挪用析构函数后获得的指针,析构函数从存储单元中移去对象
#include <cstdio>#include <cstdlib>using namespace std;void* operator new(size_t sz){ printf("operator new:%d Bytes\n",sz); void* m = malloc(sz); if(!m) puts("out of memry"); return m; }void operator delete(void* m){ puts("operator delete"); free(m); }class S{ int i[100];public: S(){puts("S::S()");} ~S(){puts("S::~S()");} };int main(int argc, char *argv[]) { int * p = new int(47); delete p; S* s = new S; delete s; S* sa = new S[3]; delete [] sa; return 0; }//:outputoperator new:4 Bytesoperator deleteoperator new:400 Bytes S::S() S::~S()operator deleteoperator new:1208 Bytes S::S() S::S() S::S() S::~S() S::~S() S::~S()operator delete
这里运用printf(),puts()等函数,而不是iostreams,由于运用iostreams对象时(全局对象cin,cout,cerr),挪用new分派内存,printf不会进入死锁状况,它不挪用new来初始化本身
类重载new和delete
//p328
重要头脑:运用static数组以及一个bool数组,返回static数组的下标地点,举行new,获得static下标数组举行delete
void* operator new(size_t) throw(bad_alloc);
void operator delete(void*);
为数组重载new和delete
//p331
定位new/delete
//p333
重要头脑:运用new运算符重载,然则不对delete运算符重载,指定内存位置new
void* operator new(size_t,void*);
继承与组合(Inheritance&Composition)
初始化表达式
成员对象假如没有默许的组织函数,必需显现的举行初始化,即在组织函数初始化列表中举行初始化
只要实行成员范例初始化以后,才会进入组织函数
没有对一切成员以及基类对象的组织函数挪用之前,若基类没有默许组织函数则必需初始化,即在初始化列表中举行初始化,不然没法进入组织函数体
组织函数和析构函数挪用的递次
组织函数挪用的递次:起首挪用基类组织函数,然后挪用成员对象的组织函数,挪用成员对象组织函数的递次是根据成员对象在类中声明的递次实行,末了挪用本身的组织函数
析构函数挪用序次与组织函数挪用序次相反,先挪用本身的析构函数,在挪用成员函数的析构函数,末了挪用基类析构函数
关于多重继承,组织函数挪用递次为继承时的声明递次
非自动继承的函数
组织函数
析构函数
operator=
继承与静态成员函数
静态成员函数与非静态成员函数的配合特性:
1. 均可以被继承到派生类中
2. 从新定义一个静态成员,一切基类中的其他重载函数会被隐蔽
3. 假如我们转变了基类中的函数的特性,一切运用该函数名字的基类版本将会被隐蔽。
4. 静态成员函数不可以是虚函数
私有继承
运用私有继承是为了不许可该对象的处置惩罚像一个基类对象,平常private更适合于组合
私有继承成员公有化
class Base{ public: Base(){} ~Base(){} void name() {cout << "Base name" <<endl;} } class Derived:private Base{ public: Derived(){} ~Derived(){} using Base::name; //私有继承公有化}
向上范例转换和拷贝组织函数
class Base{ public: Base(){} Base(const Base&){} ~Base(){} void name() {cout << "Base name" <<endl;} } class Derived:public Base{ public: Derived(){} Derived(const Derived& d):Base(d){ }//这里是挪用基类的拷贝组织函数 ~Derived(){} }
基类拷贝组织函数的挪用将一个Derived援用向上范例转换成一个Base援用,而且运用它来实行拷贝组织函数,向上范例转换是平安的
运算符重载
+=,-=,*=,/=,=等一类运算符重载
起首举行自我搜检(是不是对本身赋值),即this == &val,在举行运算,“=”运算符只许可作为成员函数举行重载
函数参数和返回值申明
关于任何函数参数,假如仅须要从参数中读而不转变它,默许地应当作const援用通报。平常算数运算符(像“+”,“-”)和bool运算符不会转变参数,所以const援用为重要通报体式格局,当函数时成员函数时,就转换为const成员函数。只要会转变左边参数的运算符赋值(如+=)和operator=,左边参数不是常量,但因参数将被转变,所以参数依然按地点通报
返回值范例取决于运算符的细致寄义,假如运用该运算符发作一个新值,就须要发作一个作为返回对象的新对象。比方operator+必需生成一个操纵数之和的对象,该对象作为一个常量经由过程返回值返回,所以作为一个左值不会被转变
一切赋值运算符均转变左值,为了使赋值效果能用于链式表达式(如a=b=c),应当可以返回一个方才转变了的左值的援用。但该援用并不是肯定假如常量援用,如(a=b).f(),这里b赋值给a,a挪用成员函数f,因而一切赋值运算符的返回值关于左值应当黑白常量援用(假如是常量援用,则f成员函数必需为const函数,不然没法挪用该函数,这与愿望相违犯)
关于逻辑运算符,人们最少愿望获得一个int返回值,或许最好是bool值
Prefix ++ & Postfix ++
成员函数
const Object& operator++(){}const Object operator++(int){}
友元函数
const Object& operator++(Object& obj){}const Object operator++(Object& obj,int){}
关于友元函数重载来讲,由于传入的Object对象被转变,所以运用非常量援用
前缀经由过程援用返回,后缀经由过程值(暂时对象)返回,由于后缀返回暂时对象,所以后缀经由过程常量值返回,前缀返回援用,假如愿望可以继承转变对象则返回援用,不然经由过程常量援用返回比较适宜,如许与后缀坚持了一致性
常量传值返回与返回值优化
作为常量平常经由过程传值体式格局返回。斟酌二元运算符+,假设在表达式f(a+b)中运用,a+b的效果变成一个暂时对象(Object),该对象被f()挪用,由于它为暂时的,所以自动被定义为常量,所以不管是不是返回值为常量都没有关联。然则假如运用(a+b).f(),这里设返回值为常量划定了关于返回值只要常量成员函数才够挪用
返回值优化经由过程传值体式格局返回要竖立的的新对象时,注重运用的情势,如operator+
version 1:return Object(lObj.i+rObj.i); version 2: Object tmp(lObj.i+rObj.i);return tmp;
version 2将会发作三件事,起首竖立tmp对象,然后挪用拷贝组织函数把tmp拷贝到外部返回值的存储单元中,末了当tmp在作用域的末端时挪用析构函数
version 1编译器直接将该对象竖立在外部返回值的内存单元,不是整的竖立一个部分变量所以仅须要一个平常的组织函数挪用(不须要拷贝组织函数),且不会挪用析构函数,效率高。这类体式格局被称为返回值优化。
operator[]
该运算符必需是成员函数,而且只接收一个参数,可以返回一个援用,可以用于等号左边。
operator->(指针间接援用运算符)
该运算符肯定是一个成员函数,它必需返回一个对象(或许援用),该对象也有一个指针间接援用运算符;或许必需返回一个指针,被用于挑选指针间接援用运算符箭头所指的内容
class Obj{ static int i,j; public: void f() const {cout<<i++<<endl;} void g() const {cout<<j++<<endl;} };int Obj::i = 47;int Obj::j = 11;class ObjContainer{ vector<obj* >a; public: void add(Obj* obj) {a.push_back(obj);} friend class SmartPointer; };class SmartPointer{ ObjContainer& oc; int index; public: SmartPointer(ObjContainer & obj):oc(obj){ index = 0; } bool operator++(){ if(index >= oc.a.size()) return false; if(oc.a[++index] == 0) return false; return true; } bool operator++(int){ return operator++(); } Obj* operator->() const{ return oc.a[index]; } };
指针间接援用运算符自动的为用SmartPointer::operator->返回的Obj*挪用成员函数
class Obj{ static int i,j; public: void f() const {cout<<i++<<endl;} void g() const {cout<<j++<<endl;} };int Obj::i = 47;int Obj::j = 11;class ObjContainer{ vector<obj* >a; public: void add(Obj* obj) {a.push_back(obj);} class SmartPointer; //声明友元之前必需示知该类存在 friend SmartPointer; class SmartPointer{ ObjContainer& oc; int index; public: SmartPointer(ObjContainer & obj):oc(obj){ index = 0; } bool operator++(){ if(index >= oc.a.size()) return false; if(oc.a[++index] == 0) return false; return true; } bool operator++(int){ return operator++(); } Obj* operator->() const{ return oc.a[index]; } }; SmartPointer begin(){ return SmartPointer(*this); } };
operator->*
//p294
运算符成员函数基础方针
一切一元运算符发起为成员
=()[]->->*必需为成员
+= -= /= *= ^= &= |= %= >>= <<=发起为成员
一切其他二元运算符为非成员
copy-on-write
//p301援用计数
自动范例转换
组织函数转换:组织函数能把别的一个范例对象(或许援用)作为它的单个参数,该组织函数许可编译器实行自动范例转换
运算符转换:运算符重载,竖立一个成员函数,该函数经由过程关键字operator后跟随想要转换的范例的要领,将当前范例转换为愿望的范例,自动范例转换只发作在函数挪用值中,而不在成员挑选时期
class Three{ int i; public: Three(int ii = 0,int = 0):i(ii){} }; class Four{ int x; public: Four(int xx):x(xx){} operator Three() const {return Three(x);} };void g(Three){}int main(){ Four four(1); g(four); g(1); }
二义性毛病
class Orange; class Apple{ public: operator Orange() const; }; class Orange{ public: Orange(Apple); };void f(Orange){}int main(){ Apple a; // f(a); error:二义性毛病}
扇出毛病:供应不止一品种型的自动转换
class Orange{};class Pear{};class Apple{ public: operator Orange() const; operator Pear() const; };void eat(Orange);void eat(Apple);int main(){ Apple a; //eat(a); error:扇出毛病}
相干文章:
【读书笔记】通晓CSS 第二版
《深切明白bootstrap》读书笔记:第一章 入门预备
相干视频:
李炎恢PHP视频教程第一季
以上就是Thinking in C++ 第一卷浏览全书笔记重点总结的细致内容,更多请关注ki4网别的相干文章!