【C++ Primer】用于大型程序的工具
1. 异常处理
异常以类似于将实參传递给函数的方式抛出和捕获。异常可以是可传给非引用实參的随意实參的类型,这意味着必须可以复制该类型的对象。
当抛出一个表达式的时候,被抛出对象的静态编译时类型将决定异常对象的类型。
抛出指针一般是个坏主意。
栈展开:沿着嵌套函数调用链继续向上,直到为异常找到一个catch子句。
栈展开期间。释放局部对象所用的内存并执行类类型局部对象的析构函数。
一个块能够通过new动态分配内存。假设该块因异常而退出。编译器不会删除该指针。已分配的内存将不会释放。
析构函数应该从不抛出异常。假设真的抛出自己的未经处理的还有一个异常,将会导致调用terminate函数,然后terminate函数将调用abort函数,使得程序非正常退出。
构造函数发生异常,要保证会适当地撤销已构造的成员。
对于未被匹配的异常,程序会调用库函数terminate。
catch子句中的异常说明符(exception specifier)看起来像仅仅包括一个形參的形參表,异常说明符是在其后跟一个(可选)形參名的类型名:catch(const runtime_error &e)
catch子句中的异常说明符必须是全然类型,不能够为前置声明,由于你的异常处理中经常要訪问异常类的成员。例外:仅仅有你的catch子句使用指针或者引用接收參数。而且在catch子句内你不訪问异常类的成员,那么你的catch子句的异常说明符才干够是前置声明的类型。
catch的匹配过程是找最先匹配的。不是最佳匹配
catch的匹配过程中。对类型的要求比較严格。不同意标准算术转换和类类型的转换。(类类型的转化包含两种:通过构造函数的隐式类型转化和通过转化操作符的类型转化)
又一次抛出(rethrow):将异常传递给函数调用链中更上层的函数(而不是同一个函数的下一个catch)。 仅仅用个空throw语句: throw;
仅仅能出如今catch或者从catch调用的函数中。假设在处理代码不活动时碰到空throw,就调用terminate函数。
被抛出的异常是原来的异常对象。而不是catch形參。
catch能够改变它的形參。在改变形參之后。假设catch又一次抛出异常,仅仅有当异常说明符是引用的时候。才会传播那些改变。
能够使用catch(...){}来捕获全部异常,假设与其它catch子句结合使用,它必须是最后一个。否则,不论什么它后面的catch子句都不能被匹配。
要想处理来自构造函数初始化式的异常。唯一的方法是将构造函数编写为函数測试块:
A : : A(int a )
try : mem(a), use(new size_t(1))
{
// 函数体
}catch(const std::bad_alloc &e)
{handle_out_of_memory(e);}
exception类型定义的唯一操作是一个名为what的虚成员,该函数返回const char* 对象。一般返回用来在抛出位置构造异常对象的信息。
1、auto_ptr为标准库提供的“资源分配即初始化”类。是接受一个类型形參的模板,它为动态分配的对象提供异常安全特性。
在memory头文件里定义。
2、auto_ptr操作
auto_ptr<T> ap; |
创建名为 ap 的未绑定的 auto_ptr 对象 |
auto_ptr<T> ap(p); |
创建名为 ap 的 auto_ptr 对象。ap 拥有指针 p 指向的对象。 该构造函数为explicit |
auto_ptr<T> ap1(ap2); |
创建名为 ap1 的 auto_ptr 对象。ap1 保存原来存储在ap2 中的指针。 将全部权转给 ap1,ap2 成为未绑定的auto_ptr 对象 |
ap1 = ap2 |
将全部权 ap2 转给 ap1。删除 ap1 指向的对象而且使 ap1指向 ap2 指向的对象,使 ap2 成为未绑定的 |
~ap |
析构函数。删除 ap 指向的对象 |
*ap |
返回对 ap 所绑定的对象的引用 |
ap-> |
返回 ap 保存的指针 |
ap.reset(p) |
假设 p 与 ap 的值不同,则删除 ap 指向的对象而且将 ap绑定到 p |
ap.release() |
返回 ap 所保存的指针而且使 ap 成为未绑定的 |
ap.get() |
返回 ap 保存的指针 |
auto_ptr仅仅能用于管理从new返回的一个对象,它不能管理动态分配的数组。
当auto_ptr被复制或赋值的时候,有不寻找的行为,因此不能将auto_ptr存储在标准库容器类中。
3、每一个auto_ptr对象绑定到一个对象或者指向一个对象。当auto_ptr对象指向一个对象的时候。能够说它“拥有”该对象。当auto_ptr对象超出作用域或者另外撤销的时候,就自己主动回收auto_ptr所指向的动态分配对象。
auto_ptr是能够保存不论什么类型指针的模板。
演示样例
void f() { auto_ptr< int > new int (42)); // // } // |
4、注意到,接受指针的构造函数为explicit构造函数,所以必须用初始化的直接形式来创建auto_ptr对象。
演示样例
// error: constructor auto_ptr< int > new int (1024); auto_ptr< int > new int (1024)); // |
pi所指的由new表达式创建的对象在超出作用域时自己主动删除。
5、auto_ptr的主要目的是在保证自己主动删除auto_ptr对象引用的对象的同一时候,支持普通指针式行为。
演示样例
auto_ptr<string> ap1( new string( "Hellobaby!" )); *ap1 = "TRex" ; // string s = *ap1; // if (ap1->empty()) // |
6、auto_ptr对象的赋值和复制是破坏性操作行为,与普通指针的复制和赋值有差别。
普通指针赋值或复制后两个指针指向同一对象。而auto_ptr对象复制或赋值后,将基础对象的全部权从原来的auto_ptr对象转给副本,原来的auto_ptr对象重置成为未绑定状态。
因此。auto_ptr赋值的左右操作数必须是可改动的左值。
由于标准库容器要求在复制或赋值后两对象相等。所以auto_ptr不能存储在标准容器中。
7、默认情况下。auto_ptr的内部指针值置为0。
8、測试auto_ptr对象
auto_ptr 类型未定义到可用作条件的类型的转换。相反,要測试auto_ptr 对象,必须使用它的 get 成员。该成员返回包括在 auto_ptr 对象中的基础指针。
演示样例
// error: cannot use an if (p_auto) *p_auto // revised test to guarantee p_auto refers to an object if (p_auto.get()) *p_auto |
应该仅仅用 get 询问 auto_ptr 对象或者使用返回的指针值,不能用 get 作为创建其它 auto_ptr 对象的实參。
9、reset操作
不能直接将一个地址(或其他指针)赋给auto_ptr对象。
演示样例
#include <iostream> #include "memory" using namespace std; int main() { auto_ptr< int > new int (123)); //p_auto if (p_auto.get()) *p_auto else p_auto.reset( new int (1042)); return 1; } |
正如自身赋值是没有效果的一样,假设调用该 auto_ptr 对象已经保存的同一指针的 reset 函数。也没有效果,不会删除对象。
10、正确使用auto_ptr类。有例如以下限制:
1)不要使用auto_ptr对象保存指向静态分配对象的指针。否则。当auto_ptr对象本身被撤销的时候,它将试图删除指向非动态分配对象的指针,导致没有定义的行为。
2)永远不要使用两个 auto_ptrs 对象指向同一对象,导致这个错误的一种明显方式是,使用同一指针来初始化或者 reset 两个不同的 auto_ptr 对象。
还有一种导致这个错误的微妙方式可能是,使用一个 auto_ptr 对象的 get 函数的结果来初始化或者 reset还有一个 auto_ptr 对象。
3)不要使用 auto_ptr 对象保存指向动态分配数组的指针。当auto_ptr 对象被删除的时候,它仅仅释放一个对象—它使用普通delete 操作符,而不用数组的delete
[] 操作符。
4)不要将 auto_ptr 对象存储在容器中。容器要求所保存的类型定义复制和赋值操作符。使它们表现得类似于内置类型的操作符:在复制(或者赋值)之后。两个对象必须具有同样值。auto_ptr 类不满足这个要求。
异常说明
1)定义
演示样例
void recoup( int ) throw (runtime_error); void no_problem() throw (); //空说明列表指出函数不抛出不论什么异常 |
异常说明是函数接口的一部分,函数定义以及该函数的随意声明必须具有同样的异常说明。假设函数声明没有指定异常说明。则该函数能够抛出随意类型的异常。
编译器在编译时不能也不会试图验证异常说明。
假设函数抛出了没有在其异常说明中列出的异常,就直接调用unexpected函数终止程序。
当某个类的析构函数声明了不抛出异常时,另外一个类继承该类时,而且有成员时,也必须将析构函数声明为不抛出异常。
基类的虚函数的异常说明能够与派生类中相应虚函数的异常说明不同,但派生类虚函数的异常说明应当更严格。
基类中虚函数异常列表是派生类异常列表的超集。
能够在函数指针的定义中提供异常说明。在用还有一指针初始化带异常说明的函数的指针。或者将后者赋值给函数地址的时候,两个指针的异常说明不必同样,可是,源指针的异常说明必须至少与目标指针的一样严格。
2. 命名空间
给定作用域中出现的名字冲突问题叫做命名空间污染问题(namespace pollution)。
命名空间可以划分全局命名空间。
形如:
namespace cplusplus_primer{
...
}
命名空间能够在全局作用域或者其它作用域内部定义。但不能在函数或类内部定义。
命名空间不能以分号结束。
命名空间外部的代码必须指出名字定义在哪个命名空间: xxspace::Query q; 也能够使用using namespace **直接说明。
命名空间能够是不连续的,多个命名空间能够累积定义。可是,假设命名空间的一个部分须要定义在还有一个文件的名字。仍然必须声明该名字。
利用这个性质能够实现接口和实现的分离。
能够在命名空间外部定义命名空间成员,返回类型以及函数名必须指出名字限定。类似于在类外部定义类成员函数:
cplus:: Sales_item cpuls:: operate + ( const Sales_item & l, const Sales_item & r),可是不能定义在不相关的命名空间中。
全局命名空间 ::member_name
未命名的命名空间的定义局部于特定文件。从不跨越多个文本文件。当中定义的名字能够直接使用。
假设在文件最外层定义域中定义未命名的命名空间。则当中名字必须与全局作用域中定义的名字不同
int i;
namespace {
int i; // error, 重定义
}
在引入命名空间之前,程序必须将名字声明为static,使得他们局部于一个文件。可是c++推荐使用未命名的命名空间来取代。
命名空间别名: namespace a = cplus; namespace b = cplus::m;
using声明一次仅仅引入一个命名空间成员: using std::vector;
using指示使得特定命名空间的全部名字可见: using namespace std;
相对于依赖于using指示,对程序使用using声明更好。能够避免名字冲突,除了在函数或其它作用域内部,头文件不应该包括using指示或using声明。
实參相关的查找与类类型形參:
考虑以下的简单程序:
std::string s;
getlint(std::cin, s);
为什么能够不加std::限定符或者using声明就使用getline呢?
编译器会在当前作用域、包括调用的作用域以及定义cin类型和string类型的命名空间查找匹配的函数。
接受类类型形參(或类类型指针及引用形參)的且与类本身定义在同一个命名空间中的函数(包含重载操作符),在用类类型对象(或类类型指针及引用)作为实參的时候是可见的。
所以std::string s; std::cin >> s; 中的操作符不用再加作用域限定符了。
隐式友元声明与命名空间:
当一个类声明友元函数(非成员函数)的时候,函数的声明不必是可见的。假设不存在可见的声明,友元声明具有将该函数或类的声明放入外围作用域的效果。假设类在命名空间内部定义。则没有另外声明的友元函数在同一个命名空间中声明。
namespace A
{
class C{
friend void f(const C&); // 相当于在A中进行了声明,
}
}
void f2()
{
A::C cobj;
f(cobj); // 这里能够不加命名空间限定符就是由于实參相关查找与类类型形參的原则。
}
可能存在异常的程序以及分配资源的程序应该使用类来管理这些资源。使用类管理分配和回收能够保证假设发生异常就释放资源。这一技术常称为“资源分配即初始化”RAII
3. 多重继承(multiple inheritance)与虚继承
class Bear: public ZooAnimal{};
class Panda: public Bear, pulic Endangered{};
多重继承构造的次序:
基类构造函数依照基类构造函数在类派生列表中的出现次序调用:在上例中,即是ZooAnimal, Bear, Endangered
多重继承析构的次序:与构造的次序相反
多个基类之间的转换:
对于派生类来说。基类之间都是等价的,假设存在两个函数
void print(const Bear&);
void print(const Endangered&);
调用print(Pandar()) 是会出现二义性的,会产生编译错误
假定全部基类都将他们的析构函数适当定义为虚函数,那么,不管通过哪种指针类型删除对象,虚析构函数的处理都是一致的:
delete pbear;
delete pzooAnimal;
赋值操作符与复制构造函数的行为与构造函数类似,构造的顺序也一样
多个基类easy导致二义性:
假定Bear与Endangered中都定义了print成员,可是Panda中未定义,则panda.print()在编译时会出错,由于不知道调用哪个版本号
假设两个继承的print函数有不同的形參表,相同会错误发生。
假设再ZooAnimal中定义了print而Bear类中未定义,也会出错。
避免二义性的方法有:自己明白指定使用哪个,如 panda.Bear::print()。或者直接在Panda中定义一个print版本号。
虚继承:
在虚继承中,对给定虚基类,不管该类在派生层次中作为虚基类出现多少次。仅仅继承一个共享的基类子对象。共享的基类子对象称为虚基类(virtual base class):
class B: class virtual A{};
class C: virtual class A{};
class D: public B, public C{};
使用虚函数的多重继承层次比没有虚继承的引起更少的二义性。
a:假设在每一个路径中x表示同一虚基类成员,则没有二义性。由于共享该成员的单个实例;
b:假设在某个路径中x是虚基类的成员。而在还有一路径中x是后代派生类的成员,也没有二义性,由于特定派生类实例的优先级高于共享虚基类的实例。
c:假设沿每一个继承路径。x表示后代派生类的不同成员,则该成员的直接訪问是二义性的,就像非虚多重继承层次一样,这时能够在派生类中定义一个同名成员函数,在该函数中指定限定名訪问成员x;
初始化:
为了解决反复初始化问题。从具有虚基类的类继承的类对初始化进行特殊处理。
在虚派生中。有最底层派生类的构造函数初始化虚基类。也就是由D来初始化A。假设D没有显示初始化A基类,就使用A的默认构造函数,假设A没有默认构造函数,则代码出错。
构造与析构:
不管虚基类出如今继承层次的不论什么地方。总是在构造非虚基类之前构造虚基类。
【C++ Primer】用于大型程序的工具的更多相关文章
- C++ Primer 学习笔记_95_用于大型程序的工具 --多重继承与虚继承
用于大型程序的工具 --多重继承与虚继承 引言: 大多数应用程序使用单个基类的公用继承,可是,在某些情况下,单继承是不够用的,由于可能无法为问题域建模,或者会对模型带来不必要的复杂性. 在这些情况下, ...
- C++ Primer 学习笔记_88_用于大型程序的工具 --异常处理[续1]
用于大型程序的工具 --异常处理[续1] 四.又一次抛出 有可能单个catch不能全然处理一个异常.在进行了一些校正行动之后,catch可能确定该异常必须由函数调用链中更上层的函数来处理,catch能 ...
- C++ Primer 学习笔记_87_用于大型程序的工具 --异常处理
用于大型程序的工具 --异常处理 引言: C++语言包括的一些特征在问题比較复杂,非个人所能管理时最为实用.如:异常处理.命名空间和多重继承. 相对于小的程序猿团队所能开发的系统需求而言,大规模编程[ ...
- C++ Primer 学习笔记_91_用于大型程序的工具 --命名空间
用于大型程序的工具 --命名空间 引言: 在一个给定作用域中定义的每一个名字在该作用域中必须是唯一的,对庞大.复杂的应用程序而言,这个要求可能难以满足.这样的应用程序的全局作用域中一般有很多名字定义. ...
- 【c++ Prime 学习笔记】第18章 用于大型程序的工具
大规模应用程序的特殊要求包括: 在独立开发的子系统之间协同处理错误:异常处理 使用各种库(可能包含独立开发的库)进行协同开发:命名空间 对比较复杂的应用概念建模:多重继承 18.1 异常处理 异常处理 ...
- C++ Primer 5th 第18章 用于大型程序的工具
C++大规模程序设计至少存在三个特殊要求: 错误处理 库的引入 复杂建模 以上三种对应C++语言的三种特性:异常处理.命名空间.多重继承. 异常处理 异常处理机制是一种允许偷懒的工具,在出现非正确的情 ...
- C++ 用于大型程序的工具
<C++ Primer 4th>读书笔记 相对于小的程序员团队所能开发的系统需求而言,大规模编程对程序设计语言的要求更高.大规模应用程序往往具有下列特殊要求: 1. 更严格的正常运转时间以 ...
- C/C++基础----用于大型程序的工具(异常处理,命名空间,多重继承)
独立开发的子系统间协同处理错误的能力 使用各种库(可能包含独立开发的库进行协同开发的能力) 对比复杂的应用概念建模的能力 异常处理 异常将问题的检测和解决过程分离开 当执行一个throw之后,程序控制 ...
- Linux程序分析工具:ldd和nm
ldd和nm是Linux下两个非常实用的程序分析工具.其中,ldd是用来分析程序运行时需要依赖的动态链接库的工具,nm是用来查看指定程序中的符号表信息的工具. 1 ldd 格式:ldd [option ...
随机推荐
- JS 如何准确获取当前页面URL网址信息
在WEB开发中,时常会用到javascript来获取当前页面的url网址信息,在这里是一些获取url信息的小总结. 下面我们举例一个URL,然后获得它的各个组成部分:http://i.cnblogs. ...
- Spring Boot 实用MyBatis做数据库操作
前言: 本项目基于maven构建,使用mybatis-spring-boot作为spring-boot项目的持久层框架 spring-boot中使用mybatis持久层框架与原spring项目使用方式 ...
- linux用户下的.profile文件丢失
登录用户时出现以下问题: #su - wqq-bash-4.1$ -bash-4.1$ 查看时发现环境变量文件丢失造成的 解决方法: # ls -la /etc/skel/ total 36drwx ...
- Data时间格式化
//时间戳转时间 function timeStamp2String(time) { var datetime = new Date(); datetime.setTime(time); var ye ...
- 《Android源码设计模式》--Builder模式
No1: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 No2: 在Android源码中,最常用到的Builder模式就是AlertDialog.Builder No3: ...
- HDU - 1027 Ignatius and the Princess II 全排列
Ignatius and the Princess II Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K ( ...
- 机器学习:KNN-近邻算法
一.理论知识 1.K近邻(k-Nearest Neighbor,简称KNN)学习是一种常用的监督学习. 工作机制:给定测试样本,基于某种距离度量找出训练集中与其最靠近的k个训练样本,然后基于这k个的信 ...
- 解决springboot项目中@Value注解参数值为null的问题
1.错误场景: springboot项目中在.properties文件(.yml)文件中配置了属性值,在Bean中使用@Value注解引入该属性,Bean的构造器中使用该属性进行初始化,此时有可能会出 ...
- hdu 2732 最大流 **
题意:题目是说一个n*m的迷宫中,有每个格子有柱子.柱子高度为0~3,高度为0的柱子是不能站的(高度为0就是没有柱子)在一些有柱子的格子上有一些蜥蜴,一次最多跳距离d,相邻格子的距离是1,只要跳出迷宫 ...
- ACM需要掌握算法
数据结构 栈,队列,链表 哈希表,哈希数组 堆,优先队列 双端队列 可并堆 左偏堆 二叉查找树 Treap 伸展树 并查集 集合计数问题 二分图的识别 平衡二叉树 二叉排序树 线段树 一维线段树 二维 ...