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

二十七:

(1)异常使我们能将问题的检测和解决分离开来。

当匹配不到catch时,将调用标准库函数terminate(当异常没有被捕获)

异常对象:编译器使用异常抛出表达式来对异常对象进行拷贝初始化,因此throw表达式必须拥有完全类型(只是声明如class A;不是完全类型),如果是类类型的话,相应的类必须含有一个可访问的析构函数和一个可访问的拷贝或移动构造函数。

静态类型决定了异常对象的类型。如果throw表达式解引用一个基类指针,而该指针实际指向派生类对象,则抛出的对象将被切掉一部分,只有基类部分被抛出。

特殊的catch要出现在前面,如派生类异常的处理要在基类异常的处理之前。应该把继承链最底端的类放在前面。

如果不能完全处理某个异常,需重新抛出,仍是一条throw语句,但不包含任何表达式。

如:throw;

捕获所有异常:catch(...)

(2)要处理构造函数“初始值列表”异常,可以使用“函数try语句块”

try出现在初始值列表的冒号之前,函数体后直接跟catch(..){}

noexcept要么出现在该函数所有声明和定义中,要么一次也不要出现。

函数指针声明定义 
可以有noexcept

typedef/类型别名 
不能有noexcept

需要在const/引用限定符之后,final,override和虚函数=0之前。

一旦noexcept函数抛出异常,程序就会调用terminate。

鉴于早期C++版本:等价的声明:

1)void fun(int) noexcept;

2)void fun(int) throw();

void fun(int)noexcept(true);//不会抛出异常

void fun(int)noexcept(false);//可能抛出异常。

noexcept()也是一个运算符,返回bool表示是否会抛出异常。

(3)只能严不能松:

函数指针如果声明了不抛出异常,则只可以指向不抛出异常的函数;如果显式或隐式指定可以抛出异常,则可以指向任何函数。

如果虚函数承诺不会抛出异常,则后续派生出来的虚函数也必须做出同样的承诺。

如果对所有成员和基类的操作都承诺了不抛出异常,则合成的成员是noexcept的。

(4)异常类层次图:

(5)命名空间

命名空间不可以定义在函数或者类内部。作用域后面无需分号。可以是不连续的。

1)c++11内联命名空间:可以被外层命名空间直接使用,inline namespace name{} 如:

namespacenameTemp{

#include “a.h”//a.h里面有内联的命名空间,那么对于nameTemp::的访问默认就是此命名空间

#include “b.h”

}

inline必须出现在命名空间第一次定义的地方。

2)未命名的命名空间

拥有静态生命周期。第一次使用前创建,程序结束才销毁。

可以在给定的文件内不连续,但是不能跨越多个文件。多个文件的未命名空间没关系。

变量可以直接使用,但是与全局的同名变量会有二义性。

类的作于域中,using声明只能指向基类的成员。(如构造函数)

using指示,可以在全局/局部/命名空间中,但是不能出现在类的作用域中。using指示会将成员提升到包含命名空间本身和using指示最近作用域的能力。(一次注入某个命名空间的所有名字),二义性的名字只有在使用时才会被发现,而using声明引起的二义性在声明处就可以被发现。

命名空间名字的隐藏规则有一个例外:给函数传递类类型对象时,除了常规作用域,还会查找实参类型所属的命名空间。(如果是派生类,包括基类所在命名空间也会查找)

友元只是说明了对类的访问权限,在类外使用需要重新声明。如果包含类对象参数,那么不重新声明也可以直接使用。

继承关系中,对于基类的函数可以using声明引入,再选择性覆盖。

对于命名空间中,using声明会和同名同参的函数冲突。

using指示引入相同函数也不会有冲突,只要我们调用时指定好就行。

(6)

多重继承,如果使用using声明,从多个基类中继承了相同的构造函数(形参完全相同),会有错误。如:

struct D1:publicB1,public B2

{

using B1::B1;

using B2::B2;

//要避免错误,需要为该构造函数定义自己的版本。

D1(const string &s):B1(s),B2(s){}

D1() = default;//一旦定义了自己的构造函数,则必须出现

}

合成版本的拷贝/移动/赋值函数,会自动处理基类的部分。(派生类到派生类赋值,派生类到基类赋值会有截断。)

编译器不会在派生类向基类的几种转化中进行比较和选择,在他看来,转化成任何一种基类都是一样好,没有优先级。

如果名字在多个基类中被找到,会有二义性,使用时要指定它的版本。不使用也会避免二义性。(即使参数列表不同,或者私有公有都会产生二义性)

避免二义性,还可以定义新的版本。

派生类可以直接和间接继承一个类多次。

iostream要对同一个缓冲区进行读写,要用到虚继承。虚继承的基类(共享)叫虚基类。

派生类中只有一份共享的虚基类子对象。virtual可以在继承说明符public等之前或之后。

1:B有x,D1,D2,都没有x 没有二义性

2:B有x,D1或D2有一个有x, 没有二义性,但派生类比B的x优先级高

3:B,D1,D2都有x,会有二义性

解决二义性:D重新定义x

虚基类是由最低层的派生类初始化的。

构造顺序:首先构造虚基类部分,然后按照派生列表构造直接基类

虚基类总是先于非虚基类构造,跟继承体系中的次序和位置无关。

二十八:

(1)

new表达式:

1)申请空间new(malloc/allocator)operator new函数

2)构造函数 /construct (定位new 在指定地点构造对象)

delete表达式:

3)析构函数(destroy执行析构函数)

4)释放空间delete(free/deallocate)operator delete函数

我们可以重载1)4) operator new 和 operatordelete ,全局作用域或类作用域,其中类作用域中是隐式静态的。(默认是static,不用显示指定static),以为是在对象构造之前和销毁之后,所以是静态的。

//返回类型和第一个参数(不能有默认实参)必须是这样的:(可以加参数)

//这些版本可能抛出异常

void *operator new(size_t);

void *operator new[](size_t);

void *operator delete(void *) noexcept;

void *operator delete[](void *) noexcept;

//这些版本承诺不会抛出异常

void *operator new(size_t,nothrow_t&)noexcept;

void *operator new[](size_t,nothrow_t&) noexcept;

void *operator delete(void*,nothrow_t&) noexcept;

void *operator delete[](void *,nothrow_t&)noexcept;

其中,void * operator new(size_t,void*);//这种形式只供标准库使用,不能被重新定义

定位new:在指定地址构造对象:但不分配内存,指针地址不一定是动态内存。如:new (&str) string(“nihao”)

new(place_address) type(args);//红色可省

new(place_address) type[size] {initializer list}//红色可省

调用自己的new,delete程序:

#include<iostream>

using namespacestd;

class MyClass

{

public:

int i;

};

void *operatornew(size_t size)

{

cout << "调用自己的new" << endl;

if (void *mem = malloc(size))

{

return mem;

}

else

{

throw bad_alloc();

}

}

void operatordelete(void *mem) noexcept

{

cout << "调用自己的delete" << endl;

free(mem);

}

int main()

{

cout << "main开始" << endl;

MyClass *pmy = new MyClass;

pmy->i = 20;

cout << "pmy->i = "<< pmy->i << endl;

delete pmy;

cout << "main结束" << endl;

return 0;

}

结果:

(2)运行时类型识别

1)dynamic_cast:可以将基类的指针或引用安全的转成派生类的指针或引用

dynamic_cast<type*>(e);

dynamic_cast<type&>(e);

dynamic_cast<type&&>(e);

对于指针的转换,如果失败,返回所需类型的空指针,而对于引用的转换,如果失败,抛出bad_cast异常(typeinfo头文件中)

2)typeid返回表达式的类型,(只有有虚函数的时候才能到运行时确定实际类型,否则就是静态类型)

数组就是返回数组类型,而不是指针类型。对于指针就是指针的静态编译类型,而不是指向对象的类型。

如果p是空指针,则typeid(*p)抛出bad_typeid异常

typeid操作的结果是一个常量对象的引用,对象类型是type_info(没有默认构造函数,且拷贝/移动构造函数、赋值运算符都是删除的)或它的派生类型。

(3)枚举类型

限定枚举类:enum class/struct开头,成员在作用域外不可访问(c++11),不能进行隐式类型转换成int等

不限定枚举类:省略 class/struct,成员与本身的作用域相同,可以隐式的转换。

可以用作switch

默认的类型可以在enum名字后加:类型。

前置声明:

enum intValues:unsigned long long;//不限定作用域,必须指定成员类型

enum class open_modes;//现代作用域,可以使用默认的int

(4)类成员指针

必须在*之前加上:classname::

1)数据成员指针

声明:

const stringScreen::*pdata;

auto pdata =&Screen::contents;

使用:

auto s =myScreen.*pdata;//用.*或->*运算符,先解引用然后再取值

访问控制规则对成员指针同样有效,对于pdata的使用必须位于成员或友元内部才行。

2)成员函数指针

声明:

auto pmf =&Screen::get_cursor;

这样声明的前提是该函数不接受任何实参,并且返回一个char

要声明具体指向的类型,需要自己先声明好:

char(Screen::*pmf2)(Screen::pos,Screen::pos) const;

pmf2 = &Screen::get;//取地址符不能丢

或者使用类型别名:

using Action =char (Screen::*)(Screen::pos,Screen::pos) const;

Action get =&Screen::get;

使用:

(myScreen.*pmf2)(0,0);

也可以作为函数参数,可以传递默认实参。

3)将成员函数作为可调用对象

因为成员函数指针在调用时,必须绑定到特定的对象上,所以这样的指针不支持函数调用运算符。所以不能将它传递给算法。

如:

auto fp = &string::empty;

find_if(svec.begin(),svec.end(),fp);

实际是:if(fp(*it))//错误,成员函数指针调用必须通过->*

解决办法:

<1>使用function生成可调用对象

我们需要告诉function一个事实:即empty是一个接受string参数并返回bool值的函数,通常,执行成员函数的对象被传给隐式的this形参,所以我们必须翻译该代码,使隐式的形参变成显式的。(第一个形参表示该成员是哪个对象上执行的)

function<bool (const string&)> fcn = &string::empty;

或者:

function<bool (const string*)> fcn = &string::empty;

调用时:

find_if(svec.begin(),svec.end(),fcn);

<2>使用mem_fn生成可调用对象

mem_fn让编译器推断成员的类型。

find_if(svec.begin(),svec.end(),mem_fn(&string::empty));

mem_fn生成的可调用对象,既可通过对象调用又可指针调用。

auto f =mem_fn(&string::empty);

f(*svec.begin());//传入对象,使用.*调用empty

f(&svec[0]);//传入指针,使用->*调用empty

<3>使用bind生成可调用对象

auto it =find_if(svec.begin(),svec.end(),bind(&string::empty,_1));

与function类似的地方:必须将函数中用于表示执行对象的隐式形参转换成显式的。

与mem_fn类似的是:生成的可调用对象的第一个实参即可以是string指针,也可以是string的引用。

(5)嵌套类

外层类与嵌套类的对象互相独立。

嵌套类相当于外层类的一个类型成员。可以直接使用外层类的成员,不必加类名说明符。

嵌套类里面定义的静态成员需要在外层类的作用域之外进行定义。

(6)union(默认公有)

任意时刻只有一个数据成员可以有值。某个成员赋值,其他成员是未定义的状态。不能继承或当基类,不能含有虚函数。

匿名union定义所在作用域内,它的成员可以被直接访问。不能包含受保护和私有成员,也不能定义成员函数。

如果union含有类类型成员,且该类定义了默认构造/拷贝控制成员,编译器将为union合成对应的版本并将其定义为删除的。含有union的类对应的拷贝控制成员也会使删除的。

我们可以定义一个enum或其他东西作为union判别式。(判断union此时表示的是什么类型)

(7)局部类

不能声明静态数据成员。只能访问外层作用域的类型名、静态变量、枚举成员。外面函数的普通变量不能被访问,(包括全局变量必须用::)

(8)extern “C” 连接指示

指出任意非C++函数所用的语言。

连接指示不能出现在类和函数定义的内部。

编写函数所用的语言,是函数类型的一部分,指向C函数的指针,与之相C++函数的指针是不一样的类型。

连接指示对返回类型和参数都有效。因为连接指示作用域声明语句的所有函数,所以如果我们希望给C++传入一个指向C函数的指针,必须使用类型别名。

//FC是指向C函数的指针

extern “C” typedefvoid FC(int);

//f2是一个C++函数,形参是C函数的指针。

void f2(FC *);

对连接到C的预处理器的支持:

#ifdef __cplusplus

extern “C”

#endif

int com(inti,inti);

时间: 2024-10-02 23:32:35

C++primer知识点(五)(终结C++primer)的相关文章

C++ Primer 第五版:第2章

*****C++ Primer 第五版第2章学习笔记***** *****实验代码在Red Hat 6.6或VS 2013中调试***** *****文章内容依据当前知识撰写,存在认识的局限性***** 今天学习C++ Primer 的第2章,还没有看完,先写一点看书的心得和笔记. 对象类型决定对象包含的数据和能参与的运算操作,同时还决定了存储空间大小.算术表达式中不要使用char,因为不同机器实现不一样,导致结果不同.单精度计算不一定比双精度快. C++类型不匹配时,是自动进行类型的转换. C

《C++ Primer第五版中文版》PDF高清

<C++ Primer第五版中文版>PDF高清 链接: https://pan.baidu.com/s/1C71Y9g1Jce2OBwLnfb0t0w 提取码: 8k9u 内容简介  · · · · · · 这本久负盛名的 C++经典教程,时隔八年之久,终迎来史无前例的重大升级.除令全球无数程序员从中受益,甚至为之迷醉的——C++ 大师 Stanley B. Lippman 的丰富实践经验,C++标准委员会原负责人 Josée Lajoie 对C++标准的深入理解,以及C++ 先驱 Barba

C++ Primer 第五版:第1 章

*****C++ Primer 第五版第1章学习笔记***** *****实验代码在Red Hat 6.6或VS 2013中调试***** *****文章内容依据当前知识撰写,存在认识的局限性***** 1.1 编写一个简单的C++程序 函数:依据我个人理解,C/C++的函数是一个能够完成一个功能的模块. 完整函数的组成: ①返回类型:不一定有返回值,故不一定有返回类型 ②函数名:根据名字标识完成特定功能的模块,必须存在 ③形参列表:可能没有参数传入,不一定存在 ④函数体:一个完整的函数应该是有

C++ Primer 第五版学习笔记

<C++ Primer>第五版中文版学习笔记 ? C++ Primer 第五版学习笔记

《C++ Primer 第五版》练习9.51参考答案

//Date.h #include <map> #include <string> #include <vector> using namespace std; struct Date {         explicit Date(const string & info){//检测输入格式,尝试初始化,若失败则进行errorInit             if(mymap.empty()){               initMap();         

C++ Primer(第五版)学习笔记_9_标准模板库_multimap多重映照容器

C++ Primer(第五版)学习笔记_9_标准模板库_multimap多重映照容器 多重映照容器multimap与map结构基本相同,但由于重复键值存在,所以multimap的元素插入.删除.查找都与map的方法不相同. 1.multimap对象创建.元素插入 插入元素时,需要使用insert()方法和类似pair<string,double>("Jack", 300.5)的元素结构.可以看到,重复的元素是按照插入的先后顺序排序的. #include <iostre

C++ Primer(第五版)学习笔记_5_标准模板库string(2)

C++ Primer(第五版)学习笔记_5_标准模板库string(2) 10.搜索string对象的元素或子串 采用find()方法可查找字符串中的第一个字符元素(char, 用单引号界定)或者子串(用双引号界定):如果查到,则返回下标值(从0开始计数),如果查不到,则返回一个很大的数string:npos(即:4294967295). #include <iostream> #include <stdio.h> #include <string> using nam

C++ Primer(第五版)学习笔记_3_标准模板库vector(2)

C++ Primer(第五版)学习笔记_3_标准模板库vector(2) 欢迎大家阅读参考,如有错误或疑问请留言纠正,谢谢 6.元素的插入 insert()方法可以在vector对象的任意位置前插入一个新的元素,同时,vector自动扩张一个元素空间,插入位置后的所有元素依次向后挪动一个位置. 要注意的是,insert()方法要求插入的位置,是元素的迭代器位置,而不是元素的下标. #include <iostream> #include <vector> using namespa

C++ Primer(第五版)学习笔记_1_标准模板库--快速入门

C++ Primer(第五版)学习笔记_1_标准模板库--快速入门 欢迎大家阅读参考,如有错误或疑问请留言纠正,谢谢 标准模板库(STL)提供三种类型的组件:容器.迭代器和算法,他们都支持泛型程序设计标准. 容器主要有两类:顺序容器和关联容器.顺序容器(vector.list.deque和string等)是一系列元素的有序集合.关联容器(set.multiset.map和multimap)包含查找元素的键值. 迭代器的作用是遍历容器. STL算法库包含四类算法:排序算法.不可变序算法.变序性算法

C++ Primer(第五版)学习笔记_2_标准模板库vector(1)

C++ Primer(第五版)学习笔记_2_标准模板库vector(1) 欢迎大家阅读参考,如有错误或疑问请留言纠正,谢谢 向量容器vector不但能像数组一样进行随机访问,还能在尾部插入元素,完全可以替代数组. 值得注意的是,vector具有内存自动管理的功能,对于元素的插入和删除,可以动态调整所占的内存空间. 容器vector的下标是从0开始的,如果vector容器的大小是n,则元素下标为0~n-1,这和数组的一样的.不一样的是,vector可以随时调整其大小. vector重要的方法有三个