基于c++ primer plus的读书笔记

c语言部分

基本函数构成

将数组传递为函数参数

1
int fcname(int arg[],int n)

基本输入输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- cin及其衍生函数返回一个iostream函数的引用,即支持
cin,get().get()
iostream的其他成员
- cout.put()显示字符
- cout.write()显示字符串
- cout<<flush刷新缓冲区
- cout<<endl刷新缓冲区并提供换行符
- dec,hex,oct控制输出数制
hex(cout)控制cout为16进制
- int width()返回字段宽度当前设置
int width(int i)设置字段宽度,返回以前字段宽度
只影响下一次输出
- fill()设置填充用字符
- precision()设置精度,即保留几位小数
- setf()设置各种输出格式
- 流状态stream_state(eof,fail,bad)
- cin.get(ch)读取下一个字符,跳过换行符和空白
- cin.get()读取空白和换行符
- cin.get()get的基础上读取到换行符并丢弃
- cin.read()读取内容,但不会在末尾加空字符
- cin.peek()读取输入流下一个字符但不抽取
- cin.putback(ch)把一个字符放到输入流最前

文件输入输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
写入文件
ofstream fout;//ofstream继承ostream
fout.open("hello.txt");
fout << "i'm adding sth"
|| ofstream fout("hello.txt");
读取文件也类似
fin >> ch||string
关闭流
fout.close()
fin.close()
检测文件是否打开
if (!fin.is_open())
设置文件输入输出格式
ios_base::

基本逻辑运算符

break打断循环 continue,跳到更新表达式前开始执行 非const引用的函数不接受const参数

基本数据类型

结构数组

stname;
1
2
3
4
5
6
7
8
9
10
11
stname stobj\[int x] =
{
{}
{}
}
union(类似结构,但相同数据类型只存一种)
每个指针需要一个*用于初始化
int * intlist=new int [10]
delete-new
delete []-new []
typedef typename aliasname

名称空间

1
2
3
4
5
#ifndef HNAME_H
#define HNAME_H


#endif

如果在遇到另一个定义HNAME_H的头文件时,将他忽略

作用域: 1默认情况下,函数中声明的变量作用域位于代码块内,如果函数内外都声明一个同名变量,运行至内部代码块使用内部,离开代码块使用外部 2静态变量存在于整个程序运行周期,脱离作用域后只是无法使用并不消失 代码块外声明且不带static关键字:链接性外部,可以在其他程序使用 代码块外声明,且使用static:链接性内部,可以在整个程序使用 代码块内声明,且使用static:作用域于代码块内,但始终存在//由于静态变量只可以定义一次,所以即使离开代码块后变量依旧存在,且值不变,直到下一次修改 3运算符::放置于变量前时,使用同名变量(如果有)的全局版本 4namespace{

} 无法放置于代码块内,因此默认为全局名称,可以囊括声明和定义,可以随时添加 定义于类声明的函数自动成为内联函数

c++特性

class

1声明构造函数时,尽量使用explicit(显性转换)前缀,防止隐性转换带来的问题 mutable(摆动的)前缀声明变量,表示这些变量可能在const成员函数内被更改 用const_cast<>和static_cast<>与this指针可以实现const成员函数向非const的转变,反之则是错误的

2class初始化成员时,按构造函数声明变量的顺序,因此初始化成员时最好也以此顺序初始化 如果不希望class有copy和赋值(=)操作,则应该在private里定义copy和=运算符

3基类引用可以指向派生类对象,无需进行强制类型转换

4定义于类声明的函数自动成为内联函数

5类的函数对所有对象共用,但数据则各自私有

6要创造类对象数组,该类必须有默认构造函数

7只有一个构造函数参数的构造函数可以用于类的自动转换 classname t; t=20; 如果想禁止这种转换,可以声明explicit给构造函数

8类声明中可定义对于某种基本类型的转换函数 operator int();//可声明为显式转换,尽量避免过多的转换造成二义性

9如果定义类成员参数为static,则它在程序中只有一个地址,可以被所有类成员共享 但通过static实现共享成员时,需要重新定义复制和赋值函数来避免问题

动态类的注意事项

*构造函数中如果用new初始化指针成员,则应该在析构函数中使用delete new对应delete,new[]对应delete[] 对多个构造函数,应用和析构函数兼容的new来初始化成员 重构复制和赋值运算符来实现深复制

10对于使用new创建的类,使用delete时其析构函数才会被调用 如果在使用new时,将对象地址赋予一个指针时,如果删除指针,则对应的对象会调用自己的析构函数 对与使用定位new创建的类对象,需要显式调用析构函数 object->~classname(); 且应该以创建顺序的相反顺序调用,因为后创建的对象可能依赖于前者

类继承

公有继承

class sonclassname: public fatherclassname 派生类继承了基类的公有接口和数据 但只能用基类public和protected函数访问基类私有数据 派生类可添加函数和数据成员 派生类需要自己的构造函数,并由于权限问题,其构造函数必须包括基类构造函数,并且同样可以使用成员初始化列表 指针 基类指针和引用可以在不显式转化的情况下指向派生类对象反过来却不行 虚函数

  • 对于基类和派生类的同名函数: 如果函数通过引用或指针调用,将确定使用哪种方法 ¥如果没有使用关键字virual,将根据引用类型或指针类型选择方法。 如果使用了关键字virtual,将根据引用或指针指向对象的类型选择方法
  • 构造函数不能为虚函数
  • 析构函数应当为虚函数
  • 友元函数并非类成员,但可以使用虚函数
  • **重新定义继承方式(虚实)并非重载,会覆盖掉原先的虚函数定义,如果必须重新定义,则需要重新定义使用基类虚函数

纯虚函数 virual typename func() const=0; 含有虚函数的类不能创建实例,只能用作基类

访问控制(protected) 派生类成员函数可以访问protected成员,不能访问private成员

私有继承

使用私有继承,基类的公有成员和保护成员都成为派生类的私有成员,只可以在派生类的成员函数中使用,可以实现has_a关系 私有继承访问基类方法时需要调动基类的命名空间 访问基类对象 如果要直接访问基类对象,则需要调用强制类型转化将派生类转化为基类

保护继承

保护继承时,基类的公有和保护成员都成为派生类的保护成员,基类接口在派生类中可用

通过using指令可以让私有函数被当前作用域可用

命令行参数

int main(int argc,char* argv[]) argc为参数个数 argv为参数组成的字符串

字符输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cin >> ***(读取输入中的结束字符为结束标志,会将换行符留在输入流)
getline(stringname,length)通过换行符确定输入结束点
cin.get()读取到换行符之前,不带参数则读取下一个字符(用于清除换行符)
cin.clear()清空输入流
<string>
重载符号+实现拼接
str.size()
输入字符串使用getline(cin,str)
结构数组
struct stname;
stname stobj[int x] =
{
{}
{}
}
字符函数库<cctype>
isspace(ch)测试是否空白
isalpha(ch)是否字符
isdigit()是否数字
ispunct()是否标点

1
2
3
4
cin >> ***(读取输入中的结束字符为结束标志,会将换行符留在输入流)
getline(stringname,length)通过换行符确定输入结束点
cin.get()读取到换行符之前,不带参数则读取下一个字符(用于清除换行符)
cin.clear()清空输入流

<string> 重载符号+实现拼接 str.size() 输入字符串使用getline(cin,str) 结构数组 struct stname; stname stobj[int x] = { {} {} } 字符函数库<cctype> isspace(ch)测试是否空白 isalpha(ch)是否字符 isdigit()是否数字 ispunct()是否标点

指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1,c++没有溢出检测机制,
char name[]="hello";
char c =name[10]导致未定义行为
2,用一个常量指针指向常量需要两次const
const char* const name="hhh"
如果需要一个class专属常量,则使用
static const ***
实现文件中,const int classname:: ***
对于宏:
template <typename T>
inline functionname
3,const 出现在*左侧表示被指物为常量,右侧表示指针为常量指针
4,函数名就是函数的地址
double pam(int);
double(*pt)(int);
pt即为函数指针
如果需要一个函数以相同相同返回值和参数的函数为一个参数,则可以考虑函数指针
5,内联函数不能递归
6,引用容器时,如果迭代器不引用,仍然传递临时副本o
7,函数传递指针时按值传递,当向函数传递指针时,指针是按值传递的!这意味着你可以改变被指向的数组内容,因为在调用函数时,这些元素不会被复制!这意味着你可以改变被指向的数组的内容,因为这些元素在函数被调用时并没有被复制。另一方面,如果你改变了所指向的数组,这种改变在函数之外不会持续,因为你只改变了指针的拷贝,而不是原来的指针本身。

智能指针

auto_ptr<string> 和<unique_ptr>指针采用所有权模型,对特定对象只有一个智能指针可以拥有它,只有拥有它的指针可以删除它 shared_ptr<string>追踪引用对象的智能指针数量,最后一个指针过期时才会调用delete 使用new分配内存才能使用auto_ptr,unique_ptr

异常

try_catch

1
2
3
4
5
6
7
8
9
10
11
try{
func();
}
catch(errortype e1){

}
func()
{
do sth;
throw(error_type e1);8
}

栈解退 假设try块没有直接调用引发异常的函数,而是调用对引发异常的函数进行调用的函数,则程序从引发异常的函数跳到包含try块和处理程序的函数(追踪到一个地址位于try块的返回地址) 其他异常特性

  • throw-catch类似函数参数和返回,但函数返回语句将控制器交给调用函数的函数,但throw语句将控制权向上返回到第一个这样的函数,包含能捕获相应异常的try-catch组合
  • throw语句总是生成副本,但catch参数使用基类引用能捕获所有派生类的异常对象

exception类

exception类可作为其他异常类的基类,用what的虚函数(返回一个字符串)重载来指示错误类型 失败时返回空指针的语法

1
int * pi= new (std::nothrow) int;

未捕获异常

未捕获的异常会使程序调用函数terminate(),默认情况下,terminate()调用abort()函数,可以指定terminate()调用的函数来修改其行为

一些新特性

关键字

关键字nullptr表示空指针

RTTI(运行阶段类型识别)

dynamic——cast

danamic_cast<type *> (pt) 如果可以安全将pt转化为type*指针,返回对象地址,否则返回空指针 如果对引用使用,错误时返回bad_cast异常

typeid和type_info

typeid返回对type_info对象的引用,type_ifo是定义在typeinfo的类,重载==和!=预算符,例如 typeid(obj1)==typeid(obj2)

类型转换运算符

  • dynamic_cast
  • const_cast
  • static_cast
1
2
//用于执行各种类型的数值转换static_cast <typename> (expression)
//转换是允许隐式转换时才能通过(派生类和基类可以互相转换)
  • reinterpret_cast /执行危险的转换

移动语义 通过指针转移右值的地址给新对象 或通过std::move()将左值转化为右值

someclass()=default default关键字显式声明编译器创建默认构造函数,复制构造函数 delete用于禁止类中的函数

关键字override可用于覆盖虚函数定义

匿名函数

返回类型编译器自动确定,可直接作为函数指针使用

1
[] (double x) {return x%3==0;} 

可以返回类型后置

1
[] (double x)-> double{int y = x;return y-x;}

可以给匿名函数命名