C++primer知识点(二)

十六:IO操作

IO对象无拷贝和赋值,只能引用传递,并且不能是const

函数good() 所有错误位都没置位的情况下返回true;

fail() 一般用作流使用的条件(failbit,badbit)

eof()和bad()只表示特定的错误。

例如:将failbit和badbit复位,但eofbit不变

cin.clear(cin.rdstate() &~cin.failbit & ~cin.badbit);

endl 换行并刷新缓冲区。

要每次输出都刷新缓冲区,用unitbuf   cout<<unitbuf;

cout<<nounitbuf;  回到正常的缓冲方式。

x.tie(&o); 将流x关联到流o

ostringstream允许保存字符串,最后输出,他在#include<sstream>中

相对于stirng,他能够使用>>操作东西。

#include<string>

#include<sstream>

usingnamespace std;

int main()

{

ostringstream formatted;

formatted<< " "<<
"Hello" <<endl; //这格式,这样保存,相当于string的+号

cout<< formatted.str();

return 0;

}

十七:容器

----------------支持快速随机访问-----------------

array固定大小。

vector

string

deque

----------不支持随机访问--------------

list

forward_list

----------容器适配器-------------

stack,queue – 基于deque

priority_queue – 基于 vector

array 可以进行拷贝和赋值,内置数组不行。

数组不能初始化给array,花括号也不能赋值给array对象。

array对象的赋值,两边的类型必须完全一样。

swap 交换array时间与元素个数成正比。

swap 交换其他容器,其实只交换两个容器的数据结构,常数时间内就可以完成。

每个容器都支持== 和 != ,但是<=等等不一定。

insert 插入到迭代器指定位置之前(1:迭代器可能指向尾部不存在的元素位置,2:开始位置插入的功能很有用),forward_list有自己特色的insert(insert_after)

emplace_front/emplace/emplace_back (调用对应的构造函数创建对象)

功能对应

push_front/insert/push_back

front/back(除forward_list) 返回首尾元素的引用。

capacity和reserve只适用vector和string

reserve不改变容器元素的数量,仅影响预先分配多大的空间。

resize只改变元素数量,不改变容量。

十八:泛型算法

1:头文件:numeric algorithm

2:迭代器另算法不依赖于容器,但算法依赖于元素类型的操作。

如:find用元素的==完成比较。

3:算法使用的是迭代器,不能执行容器操作,所以

算法永远不会改变底层容器的大小(不会删除和添加元素)

因为插入迭代器,默认在赋值时调用了容器算法:push_back(),所以他可以改变容器大小。

4:插入迭代器

(1)作为目的位置,可以保证算法有足够的元素空间。

如:fill_n(back_inserter(vec),10,0);

(2)赋值,会调用push_back将元素添加到容器:

如:vector<int> vec;

auto it = back_inserter(vec);

*it = 42;//实际调用了push_back;

5:谓词:一个可调用的表达式,返回能用做条件的值

bool isShorter(const string&s1,const string &s2)

{

returns1.size() < s2.size();

}

sort(vec.begin(),vec.end(),isShorter);

//stable_sort 可以保持等长元素间的字典序。

6:lambda表达式

1)

可调用对象:(1)函数 (2)函数指针(3)重载了函数调用运算符的类 (4)lambda表达式

可以理解为:未命名的内联函数(与函数不同的是,他可以定义在函数内部)

[]()->return type {}

[捕获列表](参数列表)->返回类型{函数体} ///捕获列表和函数体不能省略。

2)

lambda根据函数体默认推断返回类型,如果函数体包含任何单一return语句之外的内容,且未指定返回类型,则默认返回void,要明确返回类型,必须指定返回类型。

lambda不能有默认参数。

find_if 只接受一个一元谓词。

一:)捕获列表弥补了参数数量的问题。

二:)参数数量的问题可以用bind函数解决

捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和它所在函数之外的声明的名字。

lambda数据成员(捕获列表)在lambda对象创建时被初始化。而不是调用时。

3)

捕获可以是值捕获或者引用捕获。

可以让编译器根据lambda体中的代码推断我们要使用那些变量,为了指示编译器推断捕获列表,应在捕获列表中写一个&或=;

可以混合使用,如[=,&os],默认都是值捕获,os采用引用捕获。混合使用时,捕获列表的第一个元素必须是&或=

4)

可变lambda

要改变捕获变量的值,必须在参数列表首加个mutable,因此,可变lambda能省略参数列表。

如:

auto f = [v1]()mutable{++v1;};

5)指定lambda返回类型(必须使用尾置返回类型)

transform(vi.begin(),vi.end(),vi.begin(),[](inti)->int{ if(i<0) return –i;else return i;});//函数体不是一个单独return ,并且不是void返回,所以必须指定返回类型。

7:for_each接受可调用对象

for_each(vec.begin(),vec.end(),[](conststring *s){coust<<s<<;};

8:bind函数适配器

接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。

1)

find_if(vec.begin(),vec.end(),[sz](conststring &a){return a.size()>=sz;});

find_if(vec.begin(),vec.end(),bind(check_size,_1,sz));//check_size是一个函数,它比较_1字符串长度和sz的大小

如:auto checkfun = bind(check_size,_1,6);

string s = “hello”;

bool b1 = checkfun(s);

_n定义在placeholders的命名空间中,而placeholders定义在std中,也定义在functional头文件中。

using namespace std::placeholders

2)

bind 可以重新安排参数顺序:

如:

auto g = bind(f,a,b,_2,c,_1);

g(X,Y);相当于调用f(a,b,Y,c,X);

这样其实可以灵活运用参数顺序的不同,重排

sort(vec.begin(),vec.end(),isShorter);//正序

sort(vec.begin(),vec.end(),bind(isShorter,_2,_1));//逆序

反向迭代器也可以实现逆序

sort(vec.rbegin(),vec.rend());

3)bind绑定引用参数

ostream &print(ostream &os,conststring &s,char c){ return os<<s<<c;}

for_each(vec.begin(),vec.end(),[&os,c](conststring &s){.....});

引用捕获的代替:

for_each(vec.begin(),vec.end(),bing(print,ref(os),_1,‘ ’));

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

(1)查找:find 需要 ==

find(vec.cbegin(),vec.cend(),val);

find(ia+1,ia+4,val); 子范围查找

(2)求和:accumulate需要 +

accumulate(vec.cbegin(),vec.cend(),0);//0是初始值

(可以累加string,拼接)

(3)判断相等:equal 需要== 假定第二个序列和第一个一样长。char*竟也可以用==比较

equal(vec.cbegin(),vec.cend(),list1.cbegin());

(4)值覆盖:fill_n

fill_n(vec.begin(),vec.size(),0);

(5)拷贝 :目的序列至少与输入序列一样多

int a1[]={1,2,3};

int a2[sizeof(a1)/sizeof(*a1)];

auto ret =copy(begin(a1),end(a1),a2);//ret指向a2尾元素之后的位置

(6)替换:

replace(ilst.begin(),ilst.end(),0,42);//把0替换为42

如果希望原序列不变:

replace_copy(ilst.cbegin(),ilst.cend(),back_inserter(vec),0,42);

(7)排序:sort 需要 <

sort(vec.begin(),vec.end());

对排完序的可以去重一下:unique只能处理排好序的

auto it = unique(vec.begin(), vec.end());//it 指向不重复区域’后一个位置’的迭代器

进而可以删除

vec.erase(it,vec.end());

(8)转化

transform(vi.begin(),vi.end(),vi.begin(),[](inti){return i<0?-i:i;});

//目的位置迭代器和输入开始的迭代器相同。

(9)删除

remove_if(v1.begin(),v1.end(),[](inti){return i%2;});//删除奇数元素

//将偶数元素从v1拷贝到v2,v1不变

remove_copy_if(v1.begin(),v1.end(),back_inserter(v2),[](inti){return i%2});

(10)通用版本的sort需要随机访问迭代器,所以不能用于list和forward_list。

(11)取两个set的交集:set_intersection(s1.begin(),s1.end(),s2.begin(),s2.end(),s3)

十九:迭代器

1:插入迭代器 赋值时,调用容器操作来操作

back_inserter 使用push_back

front_inserter 使用 push_front会将插入元素的顺序颠倒过来。

inserter 使用insert :*t =val,等价于

it = c.insert(it,val);

++it;//递增it,使它指向原来的元素(所以,可以用inserter连续顺序插入)

*it,++it,it++不会做任何操作,都返回it

2:iostream迭代器

1)

istream读取的类型必须定义了>>运算符

istream_iterator<int> int_it(cin);

istream_iterator<int> int_eof;//默认初始化迭代器是:尾后迭代器

vector<int>vec(in_it,int_eof);//从迭代器范围构造vec

++in,in++ 使用元素类型定义的>>,从输入流中读取下一个值。

2)

ostream 需要类型具有<<输出运算符。

创建ostream_iterator时,可以提供(可选)第二个参数,每个输出元素后都会打印此字符串。

*out,++out,out++ 不做任何操作,都返回out

osteam_iterator<int>out_iter(cout,” “);

for(auto e:vec)

*out_iter++= e;//赋值语句实际上将元素写到cout

等同于out_iter = e;

可以调用copy打印vec中元素,比循环简单:

copy(vec.begin(),vec.end(),out_iter);

3:反向迭代器

++it指向前一个元素

rbegin/crbegin  rend/crend 指向容器尾元素和首元素前一个位置的指针。

sort逆序排序

我们只能从既支持++又支持—的迭代器来定义反向迭代器,流不支持递减,所以不能从forward_list和流迭代器创建反向迭代器。

通过调用reverse_iterator的base成员把反向迭代器转换为普通迭代器

|rit.base       |end()

--------------------------------

|               |rbegin

rit

rit和rit.base指向不同元素,rbegin和end也是不同元素

迭代器类别:(根据支持的操作不同)

1)输入迭代器

2)输出迭代器

3)前向迭代器

4)双向迭代器

5)随机访问迭代器

链表特有的操作会改变容器,如merge,splice(元素合并)

二十:关联容器 map set

有序保存:

map/multimap

set/multiset

无序

unordered_map/unordered_multimap

unordered_set/unordered_multiset

1:

关联容器,除了与顺序容器相同的操作外

1)有独有的操作,如count(val) 返回容器中val的个数

2)类型别名

3)无序容器提供一些调整哈希性能的操作。

key_type

mapped_type(只适用于map)

value_type,对于set与key_type相同,set中的关键字也是const的

对于map,为pair<const key_type,mapped_type>,我们不能改变元素的关键字,所以关键字类型是const的。

2:map 插入

对于不包含重复关键字的容器,insert和emplace 返回一个pair.

pair的first是一个迭代器,指向具有给定关键字的元素。

pair的second是一个bool值,指出插入成功还是已经在容器中。

不能对multi版本的map执行下标运算。

set也不能执行下标运算。

因为下标运算符可能插入一个新的元素,所以,只能对非const的map使用下标运算符。

3:

lower_bound(val)返回第一个与查找元素匹配的元素,如果没有,则指向第一个关键字大于val的元素。

upper_bound(val)返回指向最后一个匹配val的之后的元素。

这两个可以作为一个迭代器范围。

equal_range(val)返回一个迭代器pair。

若关键字存在:第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置。

若关键字不存在:两个迭代器指向关键字可以插入的位置。

for(auto pos =authors.equal_range(val);pos.first != pos.second;++pos.first)

cout<<pos.first->second<<endl;

4:无序容器 unordered_......

不使用比较运算符组织元素,而是使用一个哈希函数hash<key_type>生成哈希值和== 组织元素。

在存储上组织为一组桶。

性能依赖于哈希函数的质量和桶的数量和大小。

标准库为内置类型(包括指针)提供了hash模板。我们可以直接定义关键字是内置类型(包括指针)、string还是智能指针类型的无序容器。

为了使用自定义的类型定义无序容器,我们需要提供代替==运算符和哈希值计算函数。

size_t hasher(const Sales_data&sd)

{

returnhash<string>()(sd.isbn());

}

bool eqOp(const Sales_data&lhs,const Sales_data &rhs)

{

returnlhs.isbn() == rhs.isbn();

}

using SD_multiset = unordered_multiset<Sales_data,decltype(hasher)*,decltype(eqOp)*>;

SD_multiset bookstore(32,hasher,eqOp);

如果我们定义了==运算符,则可以只重载hash函数

unordered_set<Foo,decltype(FooHash)*>fllSet(10,FooHash);

无论有序还是无序容器,具有相同关键字的元素都是相邻存储的。

二十一:动态内存

智能指针(模板)

1:shared_ptr 允许多个指针指向同一对象

例:shared_ptr<int> p =make_shared<int>(32);//make_shared会构造对象

(1)

对于内置类型加不加()有区别,如果是自定义类型,则没什么区别,都是调用的默认构造函数。

int *p1 = new int;//默认初始化,*p1未定义。

int *p2 = new int();//值初始化为0,*p2值为0;

(2)括号包围的初始化器,可以用auto推断要分配的类型

auto p1 = new auto(obj);//括号中仅有单一初始化器是才可以;

(3)可以对数组元素进行值初始化,但是auto不能推断数组。

int *p1 = new int[10];//10个未初始化int

int *p1 = new int[10]();//10个值初始化为0的int

对于数组,不能在括号中给出初始化器,即,不能用auto做推断。

(4)delete执行两个动作:1:销毁对象 2:释放内存

const 对象不能被改变,但是可以被销毁(delete)

const int *p1 = new const int(10);

delete p1;

(5)接受指针参数的智能指针是explicit的,所以,不能将内置指针隐式转化为智能指针,必须使用直接初始化。

shared_ptr p1 = new int(10);//错误,函数返回也类似

shared_ptr p2(new int(10));//正确

(6)不要混用普通指针和智能指针,因为原则上,如果我们把内存的管理责任交个智能指针,那么就不应该用内置指针来访问了。因为可能已经释放了,用内置指针访问会出错。

(7)不能使用get初始化另一个智能指针,或为智能指针赋值。

使用get返回指针的代码,不能delete此指针。因为用get返回的指针初始化的智能指针与原来的是独立的,互相不知道对方的引用计数。

(8)使用异常处理程序能够在异常发生后另程序流程继续。确保资源被正确释放的方法是使用智能指针。

(9)可以在初始化shared_ptr时,指定删除器。

shared_ptr<T> p(q,fundelete);(当智能指针管理的资源不是new分配的内存时,很管用。比如连接资源);

2:unique_ptr 独占使用所指对象

(1)   unique_ptr是独占,所以,不支持拷贝和赋值

unique_ptr<string> p1(new string(“nihao”));

unique_ptr<string> p2 = p1;//错误

unique_ptr<string> p2(p1);//错误

(2)   release或reset将指针所有权从一个unique_ptr转移给另一个unique_ptr

(3)   不能拷贝unique_ptr有一个例外,我们可以拷贝将要销毁的unique_ptr,如函数要返回时。(编译器执行特殊的拷贝,移动赋值)

(4)   与重载关联容器(无序)类似,可以在尖括号类型后提供删除器。

unique_ptr<connection,decltype(endfun)*>p(&c,endfun);

3:weak_ptr 若引用,不增加引用计数,指向shared_ptr对象

不能使用weak_ptr直接访问对象,必须调用lock。lock返回一个指向共享对象的shared_ptr.

4:动态数组(并不是数组类型)

(1)new分配并初始化一个对象数组。

未得到数组类型指针,而是数组元素类型的指针。

(2)可以对数组中元素进行值初始化,方法是在大小后跟一对空括号。

delete []p,释放动态数组,元素按照逆序销毁。

(3)标准库提供了一个管理new分配数组的unique_ptr版本。必须在对象类型后加上空方括号。

unique_ptr<int[]>up(new int[10]);

up.release();//自动用delete[]销毁

因为up指向一个数组,而不是单个对象,所以不能用点或者箭头运算符,但是可以使用下标运算符。up[i] = i;

(4)shared_ptr不支持管理动态数组,若希望管理,必须提供自己的删除器。

shared_ptr<int> sp(newint[10],[](int p){delete []p;});

sp.reset();//他会调用delete[]

shared_ptr 没有定义下标运算符,并且不支持指针的算术运算,我们这样做:

*(sp.get()+i) = i;

5:若想让内存分配与对象构造分离,需要使用allocator替代new

new对于没有默认构造函数的类有限制。

智能指针,也只是一个指针,我们要注意分配空间(new,make_shared等)

时间: 2024-11-06 07:15:59

C++primer知识点(二)的相关文章

MySQL:表的操作 知识点难点总结:表完整性约束及其他常用知识点二次总结&#128580;

表操作 一 : 修改表表表表表表表表表: ALTER TABLE 语法 1. 改表名rename alter table 表名 rename 新表名 2. 增加字段add alter table 表名 add 字段名 数据类型 (完整性约束条件) add 字段名 数据类型(完整性约束条件) 3. 删除字段 drop alter table 表名 drop 字段名; 4. 修改字段 modify 字段名 数据类型 (完整约束条件); change 旧字段名 新字段名 旧数据类型 (完整性约束条件)

知识点--------二维数组

1.二维数组 模型:表格 定义:数据类型 [,] 数组名= new 数据类型[行数,列数]; int [,] a=new int[行数,列数]; 赋值: a[行下标,列下标]=10;   下标从0开始 取值: a[行下标,列下标] 2.数组的数组(锯齿数组) 定义: int [][] a=new int [行数][]; 赋值: 大数组里头放小数组 定义大数组 int [][]a=new int [行数][]; 定义小数组 int []a1=new int [列数]; int []a2=new i

C++primer知识点(一)

一: g++c1.cpp -o c1.exe -std=c++11 c++11,加入了int a = {10},的初始化形式,如果有精度损失,那么会有警告. 二: 对:const int引用=  int //const 只是针对自己来说的 错:int 引用 = constint //不符合逻辑与语法,引用不是常量,说明可以改,但引用的却是常量,矛盾了. 三: 引用的初始化(绑定)必须是类型一致:除了 1:常量引用可以是,可转换成引用的类型的. 因为是常量引用,既然不能改,编译器的实现方式是借助一

C++primer知识点(四)

二十四:面向对象 (1)数据抽象:接口实现分离. 继承:定义相似的类型 多态:以统一的方式使用.([父类]引用或指针调用虚函数 实现多态[动态绑定]) 如果表达式也不是引用,也不是指针,则动态类型永远与静态类型一致. virtual 在基类函数中说明,子类的相同函数默认都是virtual 子类参数列表(const/引用符后)后加上override,显示的注明改写基类的虚函数(帮助编译器检查) (2)虚析构函数:使释放父类引用或指针对象时,即使实际是派生类的对象,也能正确的释放.析构函数的虚属性也

C++primer知识点(三)

二十二: 1:拷贝控制操作 拷贝构造函数,拷贝赋值运算符,移动构造函数,移动赋值运算符,析构函数. 这些,在类的数据成员都能默认构造,拷贝,复制,销毁时,编译器默认都会有合成的版本. (1)   拷贝构造函数: Foo(const Foo&); 第一个参数是自身类类型的引用,额外的参数都有默认值. 几种情况下会被隐式使用,所以,不能是explicit 默认拷贝构造函数,又叫合成拷贝构造函数,也会逐元素的拷贝数组的成员. 拷贝初始化是依靠拷贝构造函数和移动构造函数来完成的. 调用的情况: 0)初始

C++primer知识点(五)(终结C++primer)

二十七: (1)异常使我们能将问题的检测和解决分离开来. 当匹配不到catch时,将调用标准库函数terminate(当异常没有被捕获) 异常对象:编译器使用异常抛出表达式来对异常对象进行拷贝初始化,因此throw表达式必须拥有完全类型(只是声明如class A;不是完全类型),如果是类类型的话,相应的类必须含有一个可访问的析构函数和一个可访问的拷贝或移动构造函数. 静态类型决定了异常对象的类型.如果throw表达式解引用一个基类指针,而该指针实际指向派生类对象,则抛出的对象将被切掉一部分,只有

Python数据分析--Pandas知识点(二)

本文主要是总结学习pandas过程中用到的函数和方法, 在此记录, 防止遗忘. Python数据分析--Pandas知识点(一) 下面将是在知识点一的基础上继续总结. 13. 简单计算 新建一个数据表df 1 import pandas as pd 2 3 df = pd.DataFrame({"地区": ["A区","B区", "C区"], 4 "前半年销量": [3500, 4500,3800], 5

集合知识点(二)

HashMap 格式: Map<key,values> map = new HashMap<>(); //对于HashMap集合需要传入两个参数叫做键.值 增加: map.put(key,values); //根据键的Hash码存储,如果key的Hash值相同,则根据书写顺序后面的values覆盖前的values 获得值: //获得所有的key值 set<key的类型> keys = map.keySet(); //获得所有的values值 Collection<

DotNet知识点二

19.什么是类型? 用来定义某一种数据在内存里开辟空间的大小,还可以预置操作此种类型数据的相关方法 20.this关键字在方法中使用时所代表的含义 this指的是当前类的对象,或者父类的类的对象(base只能指向父类的对象) 21.里氏替换原则 子类替换父类所在的位置 22.C#中的数据类型 值类型 简单类型(数字类型(int,short,long,float,double),字符(char),逻辑值(bool)),结构体(struct),枚举(enum)引用类型字符串(string),数组[