Skip to content

Latest commit

 

History

History
1340 lines (751 loc) · 29.9 KB

2020-9-24-cpp.md

File metadata and controls

1340 lines (751 loc) · 29.9 KB

cpp知识点摘录

来自 C++Primer5th中文版没有plus 轮子哥强势审校

由于已经会c、Java 所以与C语言一样的内容不再提及。适合:会C语言的人快速入门 持续更新

左右值

左值lvalue:有名字的变量,有地址的 右值rvalue:生命只存在这句话中

引用

int a=10,&x = a; x为a的别名,类似于指针的封装。可以用于函数形式参数f(int &x)修改x。

不能修改绑定对象。引用赋值实际是为对象赋值。

底层 顶层const

int i = 0;
int *const p1 = &i;//顶层const 不能改p1
const int ci = 41;
const int *p2 =&ci;//底层const 指向一个const int
const int &r = ci;//引用都是底层const
//顶层 *在const左侧 底层 *在右侧

constexpr

constexpr:检测变量的值是否是一个常量表达式 constexpr int x = 10;编译时就得到计算 constexpr函数

using

别名 using interger = int; 类型别名不能简单替换

typedef char *pstring
const pstring cstr = 0;
const char *cstr = 0;
//二者不等价,前者cstr不可变,后者cstr指向的char不可变

库函数基本属于命名空间std,需显示标明std::cin。 可以用using std::cin或者 using namespace std;

头文件一般不应该使用using

auto

auto 让编译器去推断表达式所属类 int * ptr1 = 0; auto ptr2 = ptr1; auto忽略引用、顶层const 如果希望auto是个顶层const 需明确指出 const auto f = ci; 还可以将引用设为auto auto &g = ci;

decltype

decltype 类型指示符 编译器分析表达式得到他的类型,却不实际计算表达式的值。 decltype(f()) sum = x; decltype 返回const 和 引用在内的类型

//cpp primer里的一段例子
//看来cpp里的指针与引用有关,解引用实际上得到的是引用
int i = 42,*p=&i;
decltype(*p) c;//错误:c是int &,必须初始化

decltype((i)) d;//错误:双层嵌套时

string

cin>>s1;//cin读空格之前的内容,空格不读进去 getline(cin,s1);//读一整行,换行读进去 不存

string.size()返回size_type类型,无符号整数,尽量不要与int混用

比较的是字典顺序

相加时,需确保加号左右有一个是string

头文件

C name.h ->cpp cname,在cname中定义的名字属于std,而name.h则不然

vector

push_back() 添加

不能用下标添加元素

vector的类型总是包括元素的类型 如vector< int>::szie_type

初始化

拷贝初始化= 只能提供一个值 类内初始值 只能使用拷贝初始化或者花括号形式 初始元素值的列表,只能使用花括号进行列表初始化,不能使用括号

括号初始化应该是类似 多态 调用函数进行初始化

迭代器

可以理解为指针的封装 begin()返回第一个元素 end()返回尾元素下一个。若元素为空,则两者相同

只有string、vector等标准库类型有下标运算符,但是所有标准库容器的迭代器都定义了 == 和 !=,大多数都没定义<,所以一般使用 前者在for循环中

迭代器类型 iterator/const_iterator,一般使用auto

cbegin(),cend()一定会返回const_iterator,begin() end()视常量而定。

解引用 (*it).empty(),括号不可省略。或者可以使用箭头运算符->

vector 增长会使迭代器失效,也不能在for中增长。

部分迭代器定义了 difference_type的带符号整形数,来用作两迭代器相减。

数组

int *ptrs[10];//ptrs是一个含有10个整形指针的数组
int &refs[10] = ;//不存在引用数组
int (*Parray)[10] = &arr;//Parray指向一个含有10个整数的数组
int(&arrRef)[10] = arr;//arrRef引用一个含有10个整数的数组
int *(&arry)[10] = ptrs;//arry是数组的引用,该数组含10个指针
//由内向外 由右向左读

size_t数组下标的类型,无符号。定义在cstddef中。 像迭代器一样,库函数begin(),end()可以获取首元素指针 尾元素指针。 两指针相减的结果是ptrdiff_t类型,定义在cstddef,带符号

标准库类型如string vector下标必须是无符号,而数组内置的下标运算可以处理负值

多维数组 for-each循环 for(auto &row:ia) for(auto col:row) count<<col; 无论是否修改,第一层都需使用引用。若不使用&row,则默认初始化为指针。

++ --

尽量使用前置版本;后置版本需要先将原始值存储下来,再进行++,造成额外浪费。

auto pbeg = v.begin();
while(pbeg != v.end() && *pbeg >= 0)
    cout << *pbeg++ <<endl;

等价于 *(pbeg++),先解引用,再加加。

显示转换

double slope = static_cast< double>(j);

const char *pc; char p = const_cast<char>(pc); 去掉底层const

int *ip; char pc = reinterpret_cast<char>(ip);

传引用参数

使用引用可以避免拷贝 bool isShorter(const string &s1,const string &s2) { return s1.size()<s2.size(); }

数组形参

三者等价 int* int[] int[10]

标准库规范

void print(const *beg,const int *end)
{
    while(beg!=end);
        cout<<*beg++;<<endl;
}
print(begin(j),end(j));

数组引用形参

void print(int (&arr)[10])
{
    for(int elem:arr)
        cout<<elem<<endl;
}

二维数组

void print(int (*matrix)[10],int rowSize);
void print(int matrix[][10],int rowSize);

argv

int main(int argc,char *argv[]) argv是一个指针数组,含有argc个指针

假设程序名为prog

prog -d -o ofile data0

argv[0] = "prog" argv[1] = "-d"

可变形参

initializer_list lst;

void error_msg(initializer_list<string> il)
{
    for(auto beg = il.begin();beg != il.end(); ++beg)
        count << *beg <<" ";
    count<<end;
} 

需用花括号

列表初始化返回值

可以用返回用花括号包围的值 如果是内置类型 最多一个值

main返回的值

返回0表示成功,其他值表示失败。cstdlib头文件定义了两个返回值预处理变量 EXIT_FAILURE EXIT_SUCCESS

返回数组指针

利用类型别名 typedef int arrT[10]; using arrT = int[10]; arrT* func (int i);

int (*func(int i))[10]

尾置返回类型 auto func(int i) -> int(*)[10];

使用decltype int odd[] = {1,3,...}; decltype(odd) *arrPtr(int i);

默认实参

string screen(sz=ht(),sz=wd,char=def);

一旦某个形参赋予默认值,后面也必须有。 作用域内只能被赋予一次

只要表达类型能转换成所需类型就可以作为默认实参。局部变量不能作为默认实参。

void f2() { def = '';//改变默认形参 sz wd = 100;//没有改变 window = screen();//screen(ht(),80,''); }

内联函数

加上关键字inline,即可向编译器发出一个请求,避免函数调用开销

constexpr函数

能用于常量表达式

这本书上写 只能有一句return,实际上编译器允许有其他语句。书里是c++11,c++14支持多个

调试

assert(expr)如果表达式为假输出信息并终止程序
如果定义了NDEBUG assert将什么也不做
#define NDEBUG 等价于 CC -D NDEBUG main.c

#ifndef NDEBUG
....
#endif 

cerr<<__func__<<endl; 可以输出函数名字
还有这些变量__FILE__ __LINE__ __TIME__ __DATE__

函数匹配

多个形参的函数匹配 该函数 每个 实参的匹配都不劣于其他可行函数需要的匹配 至少有 一个 实参的匹配优于其他可行函数提供的匹配

函数指针

只需要用指针代替函数即可 bool lengthCompare(const string&,const string&); bool (*pf)(const string&,const string&);

void useBigger(const string &s1,const string &s2,bool pf(const string &,const string &)); void useBigger(const string &s1,const string &s2,bool (*pf)(const string &,const string &));

二者等价,前者默认转换成指针

cpp的class更接近于C语言的struct,而java的class更接近于C语言的指针。 cpp函数传class是副本。java函数传class是指针。cpp类可以用等于来赋值(C语言struct也可以),Java等于赋值是修改指向(C语言的指针)。cpp的class一声明就开辟了内存,而java没有 需要另外new(C语言malloc)

const 成员函数 std::string isbn() const{return this->bookNo;} const修改隐式指针的类型

类外部定义成员函数 double Sales_data::avg_price() const{}; 使用作用域运算符来说明该函数在作用域内

=default 需要默认的初始化行为

构造函数初始值列表 Sales_data(const string &s): bookNo(s),unit_sold(0),revenue(0){} 这个初始化顺序只与定义顺序有关,与这的顺序无关

编译器会替我们合成 拷贝赋值析构

访问说明符 public: private: struct 第一个访问说明符之前都是public class 第一个访问说明符之前都是private

委托构造函数 Sales_data(string s,unsigned cnt,double price):bookNo(s),units_sold(cnt),revenue(cnt*price){}

Sales_data():Sales_data("",0,0){}

先执行委托构造函数的初始值列表 函数体,再执行自己的。 友元 结构体外定义的函数若要访问 private 须在类内 以friend关键字开头声明 为了让类用户能调用友元函数,必须在友元声明外在专门对函数进行声明

友元类 如果一个类指定了友元类,友元类的成员函数可以访问此类包括非共有成员在内所有成员。不存在传递性。friend class BooKStores;记得要写class。也可以指定一个成员函数(我没试出来)

定义一个类型成员 typedef string pos;这样可以隐藏string 先定义后使用

定义在类内部的成员函数是自动内联的。也可以显示声明

可变数据成员 mutable 修饰符,哪怕是const this,也可以修改

返回*this的成员函数 可以把一系列操作连接在一条表达式

基于const的重载 Sceen &display(){} const Sceen &display() const{} 根据变量是否为const来重载

类的作用域 在类外声明函数 函数形参,在类作用域。返回值不在

隐式的类类型转换 需要使用Sales_data的地方可以用string或者istream替代,只允许一步类型转换 有可能并不有效(产生的是临时变量 可以用explicit加以阻止

默认构造函数 如果定义其他构造函数,必须包含一个默认构造函数

聚合类 所有成员public 没有定义任何构造函数 没有类内初始值 没有基类 virtual函数 可以用花括号提供成员初始值列表,顺序一致 Data vall = {0,"Anna"};

字面值常量类 数据成员必须是字面值类型 必须至少含有constexpr构造函数 构造函数一般构造函数体一般是空的。

静态成员 static 一般声明在class内,static也只出现在类内部。 定义在所有函数之外 静态成员可以是不完全类型

IO

iostate badbit系统级错误 无法再使用 failbit 可以修正的错误,goodbit为0,未发生错误

good在无错返回true clear清除错误标志位

缓冲区 刷新缓冲区 endl flush不换行 每次输出都缓冲 cout<<unitbuf; 回到正常cout<<nounitbuf; 程序异常终止,输出缓冲区是不会刷新 任何试图从输入流读数据 都会刷新输出流

tie 关联输出输入流 tie() 返回关联的流指针 tie(&ostream) 关联其他流,如果要取消关联,可以关联nullptr。一个流只能关联一个。可以多个同时关联一个

fstream ifstream 读数据 ofstream写数据 fstream读写 fstream fstrm(s) s可以是字符串 也可以是文件指针 fstream fstrm(s,mode) fstrm.open(s)

文件模式 in out app每次写操作定位结尾(append) ate打开文件定位结尾 trunc截断文件 binary二进制 以out打开 会丢弃内容,应同时指定app模式

string流 stringstream istringstream ostringstream strm.str()返回字符串 strm.str(s)s拷贝到strm

顺序容器

vector 尾部之外位置插入删除 可能很慢 deque 双端队列,支持快速随机访问。在头尾插入/删除速度很快 list 双向链表,只支持双向访问 forward_list 单向链表,只支持单向顺序访问。任何位置都快 array 固定大小数组,支持随机。不能添加删除 string 与vector相似,随机访问快。

string vector连续存储。list forward_list 类似链表。forward_list 设计目标是最好手写单向链表 deque类似string vector,但在两端添加较块

通常vector是最好选择 有很多小元素,在意空间额外开销 不要使用list forward_list 随机访问 vector deque 中间插入删改 list forward_list 头尾插入 deque 只有在输入才必须在中间插入元素,随后需要随机访问。list转vector

可以列表初始化

反向迭代器 rbegin rend

当一个容器为另一个容器拷贝,二者类型及元素必须相同 而使用迭代器 二者类型及元素可以不同

array具有固定大小 array<int,42>需指定大小,array支持拷贝

assign 类型可以不同 seq.assign(il) 将seq元素替换为il seq.assign(n,t)替换为n个t seq.assign(b,e) 换成b e两个迭代器之间的元素

swap函数,交换两个相同类型容器的内容 提供了函数成员swap 和 非函数成员 早期只有函数成员,尽量使用非函数成员

关系运算符 相同类型才能比较,类似于字典顺序

push_back 除array forward_list 都支持 push_front 仅list forward_list deque 支持 insert vector deque list string支持 list(iter, value) 插入到iter之前

insert返回值为新元素的位置

emplace_front emplace(类似insert) emplace_back 将参数传递给元素类型构造函数。 c.push_back("978-05903534", 25, 15.99) //创建一个临时变量传入 c.emplace_back("978-05903534", 25, 15.99) //直接在容器中构造

我测试了一下往容器里加类,在cpp里应该是重新开辟一个内存,然后拷贝进来。但是在java里应该是把指针修改了一下指向,没有开辟。 cpp容器类类似数组 而java容器类类似指针数组

在顺序容器中访问元素 都有front成员函数 除array都有back成员函数 返回引用 cpp可以用c[n] c.at(n)访问 返回都是引用(如果使用auto 又想改变值,应使用auto &v) 使用下标不检查越界,at检查

删除元素 pop_back forward_list不支持 pop_front vector string 不支持 erase(p)删除p,返回之后 erase(b,e)删除范围内 clear 所有

foward_list 由于添加删除 会导致前一个元素改变,所以forward_list定义了 insert_after emplace_after erase_after 同时也定义before_begin 返回一个首先迭代器 一般同时使用before_begin和begin迭代器

改变容器大小

可以用resize来增大或缩小容器

resize(n)

resize(n,t) 任何新加的为t

添加删除都可能会使指针、引用、迭代器失效

添加

vector string若存储空间重新分配,所有都失效;未重新分配,指向之后的失效

deque 首位之外 均失效。首尾,仅迭代器失效

list forward_list 仍有效

删除

vector string 之后失效

deque 首位之外 均失效。尾元素,尾迭代器失效。

list forward_list 仍有效

capacity(vector string)

shrink_to_fit()将capcity缩小到size 不保证 (deque也支持)

reserve分配至少能容纳

string

string可以用char数组初始化,需以\0结束

substr(pos, n)

额外支持insert erase下标 提供c风格insert assign替换string所有内容

append replace是erase insert简写

find 返回一个string::size_type 失败返回一个string::npos的static成员,标准库将npos定义成const string::size_type 初始值-1

find_first_of 查找任何一个字符匹配的位置

find_first_not_of

rfind逆向搜索

find_last_of find_last_not_of

数值转换

int i = 42;

string s = to_string(i);

double d = stod(s);

stod第一个非空白必须是符号或数字,可以以0x 0X开头,也可以以点 包含e E

容器适配器

适配器使某种行为看起来像另一种事物

stack(除array foward_list) queue(基于list queue) priority_queue(基于vector deque)

stack queue默认基于deque实现 priority_queue基于vector

可以给第二个类型重载默认容器

stack<string , vector < string >> str_stk;

stack方法 push pop emplace top

queue/prioity_queue方法pop front back(仅queue) top push emplace

泛型

算法一般不直接操作容器,而是使用迭代器,不会直接添加删除元素,利用特殊迭代器 插入器

一些算法从两个容器读元素,可以来自两个容器,元素类型也不严格

只读算法 find accumulate定义在numeric 前两个表示求和范围,第三个是初始值,第三个参数决定函数使用哪个加法运算,返回什么值

string sum = accumulate(v.cbegin(), v.cend(), "");//会报错

string sum = accumulate(v.cbegin(), v.cend(), string(""));

equal前两个表示第一个序列范围,第三个表示第二个序列首元素,默认第二个至少与第一个一样长

写容器元素算法

fill(vec.begin(), vec.end(), 0);

fill_n(vec.begin(), n, val);假定有n个元素,不能在空容器中使用

copy(begin(),end(),begin())返回拷贝到之后的位置

replace(begin , end ,val, val) 将等于第一个改成第二个

replace_copy保留原序列不变 需要第三个迭代器

sort(begin, end)排序

unique(begin,end)排序后,可以把相邻相同的挪到数组后面,返回最后一个不重复之后的位置

erase(end end)

插入迭代器

back_inserter

vector<int> v;
auto i = back_inserter(v);
fill_n(i,10,0);

谓词

用来定义算法 一元谓词 二元谓词

sort(begin, end, function)

sort排序 相等最好返回false

stable_sort 稳定排序

find_if 一对迭代器 一个函数 返回第一个返回值非零的元素,或者尾迭代器

for_each

lambda表达式

[capture list] (parameter list) -> return type { function body}

捕获列表是使用它所在函数的变量

可以忽略参数列表和返回类型

[ 捕获 ] ( 形参 ) { 返回 }

定义lambda也就定义了一个类和一个对象,这个类包含所捕获的变量的数据成员

被捕获对象是在创建时建立,后续修改不会影响lambda成员

可以捕获引用,有时不能拷贝对象,只能捕获引用

隐式捕获

捕获列表写 [=] [ & ]捕获引用/值,也可以混合使用[=,c]第一个元素必须= / &

值拷贝的可以用mutable来可变 [ v1 ] mutable{ v1++; }

如果包含出了return 之外的句子,默认返回值为void

需要显示写出返回类型

函数作为谓词无法捕获变量

可以使用bind库函数 在functional头文件

auto newCallable = bind(callable, arg_list);

arg_list 第一个参数 _n 表示占位第几个参数

使用占位符需先声明using namespace std::placeholders;

可以用bind来更改顺序

bind(f, a,b,_ 2, c , _ 1)

第一个参数绑到_ 1 第二个参数 _ 2

cref函数 生成一个保存const引用的类

迭代器

插入迭代器

it = t 插入t

*it it++ ++it不会做任何事,返回it

back_inserter

front_inserter 会一直在前面插入 push_front

inserter 接受第二个参数,必须时一个指向给定容器的迭代器

iostream迭代器

istream_iterator输入流

ostream_iterator输出流 不允许空

istream_iterator<int> it(cin);
istream_iterator<int> end;
vector<int> v;
while(it != end){
	v.push_back(*it++);
}

vector<int> v(it, end);

允许懒惰求值。保证在第一次解引用前,读操作已完成

	ostream_iterator<int> it(cout," ");//每个后面加个" "
	vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9};
	for (auto i : v)
		*it++ = i;//实际上 it = i;结果一样
	cout << endl;

	copy(v.begin(), v.end(), it);
	cout << endl;

反向迭代器

++往前一个 --往后一个

可以使用反向迭代器,使sort反向排序

反向转换成普通

reverse_iterator的base成员函数,返回普通迭代器。指向元素不同,因为左开右闭

五种迭代器

输入迭代器:必须支持 == != ++ * -> 单遍扫描 find accumulate istream_iterator

输出迭代器:支持++ *单遍扫描 copy ostream_iterator

前向迭代器:可以读写 只能沿一个方向,支持所有输入输出 多遍扫描 replace forward_list

双向迭代器:-- reverse 除了foward_list 容器都支持

随机访问迭代器:支持双向 < <= > >= + += -= - 两个迭代器相减 能用索引 sort array deque string vector 数组指针

参数

dest参数表示算法可以写入目的位置,不管写入多少。插入迭代器或者ostream_iterator

用重载来传递谓词代替 < ==

_if版本:接受一个元素值的算法

_copy 取分拷贝

list forward_list 定义独有的sort merge remove reverse unique

list 还定义splice splice_after融合

关联容器

有序set map

允许重复 multi

无序 unordered

map会返回pair,first成员保存关键字,seconde保存对应值

set find调用返回一个迭代器

有序容器必须定义元素比较的方法,严格弱序 可以看作小于等于

可以自定义

set< Sales_data, decltype(compareIsbn) *> bookstore(compareIsbn);

pair类型 定义在utility

成员为public first second

有关系运算符,按照字典序排序

可以用括号 花括号 make_pair初始化

key _type 关键字类型

mapped_type 关联类型

value_type set就是关键字,map是关键字-值队

关键字部分是const

解引用迭代器,会得到value_type的引用。不能改变关键字成员

迭代器按关键字升序遍历

通常不对关联容器使用泛型算法。通常是将关联容器当作一个源序列。copy inserter

insert成员函数 两个版本 一对迭代器,一个初始化列表

对map insert需要插入pair

会返回一个pair,告诉我们是否成功。第一个元素 迭代器,指向给定关键字的元素,第二个是 bool 指出是否成功。

multi则只返回迭代器

erase 一个迭代器 一对迭代器:返回void key_type:返回删除个数

下标运算

map unorderedmap提供下标运算 和 at函数,multi不支持。下标运算 如果不存在,会自动初始化。map下标返回mapped_typed,解引用为value_type

set不支持下标

count返回个数

lower_bound(k)返回第一个不小于k

upper_bound(k)返回第一个不等于k

equal_range(k)返回pair,范围

无序容器

不是用比较运算符来组织,而是使用一个哈希函数 和 ==。有序容器应该是红黑树

不是按字典序。用一个哈希函数映射到桶。可以管理桶

使用==来比较元素。hash< key_type >生成哈希 内置类型提供了hash模板

使用自定义类,需要替代== 和hash

size_t hasher(const Sales_data &sd)
{
    return hash<string>()(sd.isbn());
}
bool eqOp(const Sales_data &lhs, const Sales_data &rhs)
{
    return lhs.isbn() == rhs.isbn();
}
unordered_multiset<Sales_data, decltype(hasher)*,decltype(eqOp)*> bookstore(42, hasher,eqOp);

如果类定义==,可以只重载哈希函数

动态内存

静态内存保存局部static 类static 函数外的变量

栈内存保存函数内非static

由编译器自动创建 销毁。

还拥有一个内存池,自由空间/堆 ,动态分配

new delete

memory中定义shared_ptr unique_ptr

shared_ptr

也是模板,默认初始化保存一个空指针。

get()返回指针 swap交换

make_shared< T > (args)

reset操作

他有个引用计数,变为0,会自动释放,通过析构函数。

存放在容器,而后不需要全部元素

可以传递一个指向删除器函数的参数

可以写出类似java arraylist

class ArrayList{
private:
    shared_ptr<vector<int>> data;
public:
    ArrayList():data(make_shared<vector<int>>()){}
    int get(int index){
        return (*data)[index];
    }
    void push_back(int num){
        (*data).push_back(num);
    }
};

new delete

new 返回一个指针 可以用圆括号 也可以用花括号 auto p1 = new auto(obj); p指向与obj类型相同对象

内存不够会抛出bad_alloc,new (nothrow)int分配失败,返回一个空指针

需要自己delete

shared_ptr< int > p2 (new int(1024));

如果把内置指针给share_ptr ,会由share_ptr释放

unique_ptr

没有类似 maked_shared ,需要将其绑定到new,必须初始化,可以调用release reset,转移给另一个

不能拷贝,但是可以返回unique_ptr

auto_ptr,具有unique_ptr部分特性,不能在容器中保存,不能返回

unique_ptr<objT, delT> p (new objT,fcn);

weak_ptr

不控制生存期,指向share_ptr管理的对象,不会改变计数

lock()方法,如果有对象,返回shared,如果为空,返回空shared

动态数组

int *pia = new int[get_size()];

不能用for来处理,不能用begin end

可以用括号或者花括号初始化int *pia = new int[get_size()]();int *pia = new int[get_size()]{};

动态分配空数组是合法的

delete [] pa; 逆向销毁

可以用unique_ptr管理数组. unique_ptr<int []> up(new int[10]); up.release();支持下标

shared_ptr不支持,需要自定义删除器。shared_ptr< int> sp(new int[10], [ ] (int * p){delete [] p;});不支持下标

allocator

定义在memory

allocator< string> alloc;

auto const p = alloc.allocate(n) 分配n个未初始的string 为构造

construct 在给定位置 构造,接受多个参数,第一个表示位置

auto q = p;

alloc.construct(q++);

alloc.construct(q++,10,'c');

用完后需destory

while(q != p)

​ alloc.destory(--q);

释放内存

alloc.deallocate(p,n);

拷贝填充未初始化算法

uninitialized_copy(b,e,b2)be范围 b2未初始化

uninitialized_copy_n(b,n,b2)拷n个

uninitialized_fill(b,e,t) be范围均为t拷贝

uninitialized_fill(b,n,t)

拷贝

一个构造函数 第一个参数是自身类引用,且额外参数都有默认值,是拷贝构造函数,用来组织我们拷贝该类类型的对象

class Foo{

public:

​ Foo(Foo& f);

};

析构函数

首先执行函数体,然后销毁成员,按逆序销毁

删除函数

=delete

不能以任何方式调用

也可以声明在private

先跳过13章

重载运算

= [ ] ( ) ->必须成员

复合 一般是成员

改变状态一般是成员,递增 递减 解引用

对称性 非成员函数

输出运算符<<

第一个参数 是非常量ostream引用

第二个一般是常量引用

ostream &operator<<(ostream &os,const Book &b){
    os<<b.mon();
    return os;
}

必须是非成员函数。若为成员函数,则左侧对象将是我们类的一个对象

输入运算符>>

istream &operator>>(istream &is, Book &b)
{
    int x;
    is >> x;
    b.change(x);
    return is;
}

相等=

bool operator==(const Book &a, const Book &b)
{
    return a.mon() == b.mon();
}

+=

	Book& operator+=(const Book &other){
        money += other.money;
        return *this;
    }

下标运算符

必须是成员函数

	Book &operator[](int r)
    {
        return y[r];
    }

递增递减++ --

前置返回引用 需检查

后置返回值 额外参数int 区别前后

成员访问 * ->

-> 与*不同在于,不能改变获取成员的事实

调用运算符()

operator()(int val)

类似于lambda

这种类需为每个捕获的值建立对应数据成员

function模板

标准库定义 plus< type> minus<> multiplies< > divides< > divides <> ...

function < T> f

function < int(int,int)> 声明一个函数

map< string , function < int(int,int)>> insert时需用函数指针 或用lambda函数

类型转换运算符

operator int() const{ return val;}

可能会与用户所想不同

显示的类型转换运算符

explict operator int() const{ return val;}

要用static_cast< int>(si) 。被用于条件,还是会被隐式转换

会有二义性问题 p517

oop

基类 派生类

virtual表示派生类 需覆盖

protected 派生类能访问,派生类成员 友元函数 只能通过派生类访问

: 类派生列表 三种访问符 public protected private,用来控制继承来的范围,class是默认私有,struct默认公有

同样是通过构造函数初始化列表

类名后final表示不希望其他类继承

静态类型 动态类型 知道允许才知道类型 (当且仅当 指针调用且虚函数),能使用哪些是静态决定。用哪个是动态决定

当一个基类、一个派生类初始化/赋值,会使用基类的定义

虚函数 返回值必须相同,除非返回类本身指针或调用

final override

void f1(int) const override{ }//表示虚函数

void f1(int) const final{ }//表示不能覆盖

强迫使用某个版本

baseP -> Quote::net_price(42);

=0 纯虚函数

含有纯虚函数的类是抽象基类,不能直接创建

友元关系不能继承

可以用 using 改变个别成员可访问性

跳过 拷贝控制 P551

容器

基类容器添加派生类,派生被切掉

可以在容器中放智能指针

模板 泛型

template < typename/class T> int compare(const T &v1, const T &v2)

编译器帮我们推断

非类型模板

template < unsigned N, unsigned M> int compare(const char (&p1) [N], const char (&p2) [M])

当编译器从模板实例化一个类时,他会重写模板

成员函数没有被使用,不会被实例化

定义在模板之外须以template开始 接类模板参数列表

template < typename T>

void Blob< T> :: check(size_type i, const std::string &msg) const { }

友元

可以一对一 可以一对多

template < typename T> class Bob{

friend class BlobPtr< T>;

}

template < typename T> class Bob{

friend class BlobPtr< typename X>;

}

将模板类型定为友元

template < typename Type> class Bar{

friend Type;

}

别名

template < typename T> using twin = pair< T, T>

static 成员

每个实例都有自己的static成员

::

T::size_type * p

歧义 是相乘还是指针 ,默认不是类型

如果是指针 需要typename T::size_type * p

默认模板实参

template < typename T, typename F = less< T>>

右侧都需有默认实参

使用默认形参,需有空尖括号对

一个类可以有模板函数

显示实例化

extern

模板参数推断

const转换

数组或函数指针转换

从左至右与对应的模板参数匹配,只有最右参数才可以忽略

尾置返回

template < typename It>

auto fcn(It beg, It end) ->decltype(*beg)

(可以用标准类型转换模板 P606)