读书笔记——C++语言程序设计

第二章 C++简单程序设计

P27:字符串中的每个字符占一个字节,并在末尾添加’\0’作为结尾标记

sizeof("abc"); //4

P32:sizeof运算符用于计算某个类型的对象在内存中所占的字节数。

按位与&

00000011 & 00000101 = 00000001

按位或|

00000011 | 00000101 = 00000111

按位异或^

00111001 ^ 00100011 = 00010011

P37:"<<"是预定义的插入符,作用在流类对象cout上便可以实现最一般的屏幕输出。

cout<<表达式1<<表达式2

最一般的键盘输入是将提取符作用在流对象cin上,要求从键盘上输入两个int,以空格分隔

int a,b;
cin>>a>>b;

P53:使用typedef为一个已知的数据类型另外命名,这样有利于提高程序的可读性,给较长的类型名起一个短名。

typedef 已有类型名 新类型名表;

第三章 函数

P65:如果希望在定义一个函数前调用它,则需要在调用函数之前添加该函数的函数原型声明。这点是和java不一定的地方

类型说明符 函数名(含类型说明的形参表)

P78引用是一种特殊类型的变量,可以被认为是另一个变量的别名,通过引用名和通过被引用的变量名访问变量的效果是一样的。

P81内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入到每一个调用处,内联函数应该是比较简单的函数,结构简单、语句少。

inline 类型说明符 函数名(含类型说明的形参表) {
  语句序列
}

P82:函数在定义时可以预先声明默认的形参值,有默认值的形参,不能出现在没有默认值的形参的左边。

int add(int x=5,int y=6) {
  return x + y;
}

P87include进来的头文件,有前缀c的表示该头文件继承自标准C的头文件。为了保持对C程序的兼容性,C++中也允许继续使用这些以.h为后缀的文件。保留这些头文件仅仅是处于兼容性考虑,在编写C++程序时,应尽量使用不带.h后缀的头文件。

P90:运行栈实际上是一段区域的内存空间,与存储全局变量的空间无异,只是寻址的方式不同而已。运行栈中的数据分为一个一个栈帧,每个栈帧对应一次函数调用,栈帧中包括这次函数调用中的形参值、一些控制信息、局部变量值和一些临时数据。这看上去和Java的虚拟机栈类似)

P91:在将数据压入和弹出运行栈、确定要访问的形参和局部变量的地址是,都需要获得栈顶的地址,**因此需要一个专门的存储单元记录栈顶地址,**在1A-32中,esp寄存器就是用来记录栈顶地址的,它称为栈指针

但只有一个寄存器存储栈顶地址,有时还不够用,因为有些函数的栈帧大小是不确定的,这就会在函数返回前恢复栈指针时遇到麻烦,因此还需要另外一个寄存器保存函数刚被调用时栈指针的位置。在IA-32中这一任务由ebp寄存器来完成,它被称为帧指针。另外由于形参和局部变量相对于帧指针的位置肯定是确认的,函数的形参和局部变量地址通常通过帧指针来计算,而非栈指针

P94:C++比C更安全的原因之一在于C语言允许在调用函数前只对函数进行不完整的声明——只声明函数名和返回类型,而不声明参数类型,C语言甚至允许在调用函数前根本不对函数加以声明。

第四章 类与对象

P100:多态性是指一段程序能够处理多种类型对象的能力。在C++语言中,这种多态性可以通过强制多态、重载多态、类型参数化多态、包含多态4种形式来实现。包含多态和类型参数话多态属于一般多态性,是真正的多态C++中采用虚函数实现包含多态。虚函数是多态性的精华,模板是C++实现参数化多态性的工具,分为函数模板和类模板两种

P104:函数的原型声明要写在类体中,原型说明了函数的参数表和返回值类型。而函数的具体实现是卸载类定义之外。与普通函数不同的是,实现成员函数时需要指明类的名称。

类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员

P114:析构函数是在对象的生存期即将结束的时候被自动调用的。他的名称有类名前面加“~”构成,没有返回值。

class Clock {
  public:
  	Clock();
  	void setTime(int newH,int newW,int newS);
  	void showTime();
  	~Clock(){}
  private:
  	int hour,minute,second;
};

P129结构体是一种特殊形态的类,和类一样,可以有自己的数据成员和函数成员,可以有自己的构造函数和析构函数,可以控制访问权限,可以继承,支持包含多态等。和类唯一的区别在于,结构体和类具有不同的默认访问控制属性:在类中,对于未指定的访问控制属性的成员,其访问控制属性为私有类型(private);在结构体中,对于未指定任何访问控制属性的成员,其访问控制属性为公有类型(public)。C++引入结构体是为了保持和C程序的兼容性。

P130:如果一个结构体中只包含公共数据成员,且不包含自定义的构造方法,那么可以这么初始化结构体对象

struct Student {
  int num;
  String name;
  char sex;
  int age;
}

Student stu = {970089,"LinLin",'F',19};

131:联合体是和结构体类似的一种特殊形式的类,联合体的全部数据成员共享同一组内存单元,联合体变量中的成员同时最多有一个是有意义的。联合体不能继承,也不支持包含多态。一般联合体用来存储一些公有的数据,而不为它定义函数成员。

P137:位域是一种允许将类中的多个数据成员打包,从而使不同成员可以共享相同的字节的机制。

第五章 数据的共享与保护

P147:类作用域

  1. 如果在X的成员函数中没有声明同名的局部作用域标识符,那么在该函数内可以直接访问成员m。
  2. 通过表达式x.m或者X::m。X::m的方式用于访问静态成员
  3. 通过ptr->m这样的表达式,其中ptr为指向X类的一个对象的指针。

P148:有两类特殊的命名空间——全局命名空间和匿名命名空间。全局命名空间就是默认的命名空间,**在显示声明的命名空间之外声明的标识符都在一个全局命名 空间中。**匿名命名空间是一个需要显示声明的没有名字的命名空间,匿名命名空间常常被用来屏蔽不希望暴露给其他源文件的标识符,这是因为每个源文件的匿名命名空间是彼此不同的,在一个源文件中没有办法访问其他源文件的匿名命名空间。

//匿名命名空间
namespace {
  匿名命名空间内的各种声明
}
//using语句1
using 命名空间名::标识符名; //将指定标识符暴露在当前的作用域
using namespace 命名空间名; //将指定命名空间内所有标识符暴露在当前的作用域

P159:友元关系提供了不同类或对象的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。

P162:友元关系不能传递的,友元关系是单向的,友元关系不能被继承。

P164:如果将一个对象说明为常对象,**则通过该对象只能调用它的常成员函数,而不能调用其他成员函数。**const关键字可以用于对重载函数的区分。

void print();
void print() const;

P171:使用extern修饰变量、函数可以使外部能够访问。**使用static可以让修饰的变量或函数无法被其他编译单元引用。**在ISO C++ 2.0中,鼓励使用匿名命名空间来实现static的功能。

P173:5.6.4 编译预处理

在C语言中,用#define来定义符号常量,在C++中有更好的const来实现。

在C语言中,还可以用#define来定义带参数宏,以实现简单的函数计算,提高程序的运行效率,但在C++中这一功能已经被内联函数取代。

在C++中#define使用最多的地方是,用来表示一个宏已经定义过了。

#undef的作用是用来删除由#define定义的宏,使之不再起作用。

条件编译指令:#if、#endif、#else、#elif(else if)、#ifdef、#ifndef

defined是一个预处理操作符,而不是指令,因此不需要以#开头

第六章 数组、指针与字符串

P188:C++从C继承来的一个重要特征就是可以直接使用地址来访问内存,指针变量便是实现这一特征的重要数据类型。应用指针,可以方便地处理连续的大量数据,以较低的代价实现函数间的大量数据共享,灵活地实现动态内存分配。

P199:地址编码的基本单位是字节,每个自己由8个二进制位组成,也就是说每个字节是一个基本内存单元,有一个地址。

P200指针也是一种数据类型,具有指针类型的变量被称为指针变量。指针变量是用于存放内存单元地址的。

int *ptr; //定义一个指向int型数据的指针变量

P201:*和&出现在声明语句和执行语句中的含义是不同的,它们作为一元运算符和作为二元运算符时含义也是不同的。

string s = "abc";
string* s_p = &s;
cout << s_p << endl; //内存地址
cout << *s_p << endl; //"abc"


对于基本类型的变量、数组元素、结构成员、类的对象,可以使用取地址运算符&来获得它们的地址,例如使用&i来取得int型变量i的地址。

一个数组,可用他的名称来直接表示他的起始地址。数组名称实际上就是一个不能被赋值的指针,及执政常量

P202:可以声明指向常量的指针,这样,不能通过指针改变常量值,但是指针可以改变,指向别的变量。

还可以声明指针类型的常量,这时指针本身的值不能被改变。

P209:如果以指针作为形参,在调用时实参将值传递给形参,也就是使实参和形参指针变量指向同一个内存地址。**那么通过形参指针对数据值的改变也同样会影响着实参指针所指向的数据值。**这也很好理解,只是改变了指针指向地址上的值而已。

P211:函数指针就是专门用来存放函数代码首地址的变量。声明一个函数指针时,也需要说明函数的返回值,形参列表

数据类型 (*函数指针名) (形参表)

可以使用typedef定义函数指针,从而创建别名

typedef int (*DoubleInfFunction)(double);
DoubleIntFunction funcPtr;

P213:对象指针可以通过快捷方式访问成员变量

对象指针名 -> 成员名

P218:动态内存分配:保证程序在运行中按照实际需要申请适量的内存,使用结束后还可以释放。

new 数据类型 (初始化参数列表);
delete 指针名;

P225:用vector定义的数组对象的所有元素都会被初始化,如果数组的元素是基本数据类型,则都会被0初始化。如果是类类型,则会调用默认的构造函数。因此如果元素数组是类类型的vector,需要保证元素的类都有默认构造函数。

P227:浅复制是只复制了对象的值,指针还是指向同一个地址,所以会相互影响。并且因为指向同一块内存,所以空间会被释放两次。深复制是完全的复制,有两个不同的内存指向。

P241:引用和指针

指针是C语言的一个特性,C++继承了这个特性的同时引入了引用。指针是一种底层的机制,引用则是一种较高级的机制。在语言概念上,引用是另一变量的”别名“,把地址这一概念隐藏起来了,但是在引用运行时的实现机制中,还不得不借助于地址

引用和指针的一个显著区别是,普通指针可以被多次复制,也就是说可以多次更改它指向的对象,而引用只能在初始化时指定被引用的对象,其后就不能更改了。

对于数据参数传递、减少大对象的参数传递开销这两个用途来说,**引用可以很好地代替指针,使用引用比指针更加简洁、安全。**这就是为什么引入引用的原因,简洁安全。

第七章 继承和派生

C++是多继承的,派生类可以继承父类除了构造函数、析构函数、私有成员之外的其他成员。形如:

class 派生类名:继承方式(public/protected/private) 基类1,继承方式 基类2,继承方式 基类3...{
  派生类成员声明;
};

继承方式(public/protected/private)决定了基类成员在派生类中以何种访问修饰符出现,这决定了继续派生或者类族外的类对这些基类成员变量的访问权限

P276:虚基类

查看 网摘>虚继承与虚基类 内容

第八章 多态性

P316:一般虚函数成员

virtual 函数类型 函数名(形参表);

虚函数声明只能出现在类定义中的函数原型声明中,而不能在成员函数实现的时候。(类似Java的接口函数吧)

P322:纯虚函数与一般虚函数成员的原型在书写格式上的不同就在于后面加了"=0"。声明为纯虚函数之后,基类中就可以不再给出函数的实现部分。

virtual 函数类型 函数名(参数表) =0;

带有纯虚函数的类是抽象类。抽象类派生出新的类之后,如果派生类给出所有纯虚函数的函数实现,这个派生类就可以定义自己的对象,因而不再是抽象类;反之,如果派生类没有给出全部纯虚函数的实现,这时的派生类仍然是一个抽象类。

P335:C++的类类型分为两类——多态类型和非多态类型。多态类型是指有虚函数的类类型,非多态类型是指所有的其他类型。

P336:设计多态类型的一个重要原则是,把多态类型的析构函数设定为虚函数,因为多态类型的基类指针调用的是派生类的行为,在删除对象时,需要通过析构函数来动态的确定删除的是哪个派生类对象。非多态类型的基类指针调用的是基类本身的行为

P337:dynamic_cast是与static_cast、const_cast、reinterpret_cast并列的4种类型转换操作符之一。与static_cast不同的是,它执行的不是无条件的转换,它在转换前会检查指针(或引用)所指向的实际类型是否与转换的目的类型兼容,如果兼容转换才会发生

P339:typeid是C++的一个关键字,用它可以获得一个类型的相关信息。通过typeid得到的是一个type_info类型的常引用。type_info是C++标准库中的一个类,专门用于在运行时表示类型信息,它定义在typeinfo头文件中。type_info类有一个名为name的函数,用来获得类型的名称

const char* name() const;

此外,它还重载了"==“和”!="操作符,使得两个type_info对象之间可以进行比较,从而判断两个类型是否相同。

第九章 群体类和群体数据的组织

P347:函数模板的定义形式:

template <模板参数表>
类型名 函数名(参数表) {
  函数体的定义
}

具体示例:

#include 
using namespace std;
template
T abs(T x) {
    return x<0?-x:x;
}

int main() {
    int n = -5;
    double d = -5.5;
    cout << abs(n) << endl;
    cout << abs(d) << endl;
}

P353:线性群体中的元素次序与其位置关系是对应的。在线性群体中,又可按照访问元素的不同方法分为直接访问、顺序访问和索引访问。其实就是对应不同的数据结构而已,如:数组、链表、栈和队列等

第十章 泛型程序设计与C++标准模板库

P399:标准模板库(Standard Template Library,STL)是一个用于支持C++泛型编程的函数库,STL提供了一些常用的数据结构和算法。

P400:STL的4中基本组件:

  1. 容器,包含向量(vector)、双端队列(deque)、队列(list)、集合(set)、多重集合(multiset)、映射(map)和多重映射(multimap)
  2. 迭代器,提供顺序访问容器中每个元素的方法,需要包含头文件
  3. 函数对象,是一个行为类似函数的对象,对它可以想调用函数一样调用。需要包含头文件
  4. 算法,包含70多个算法,查找、排序、消除、计数、比较、变化等等算法。需要包含头文件

第十一章 流类库和输入输出

P481:就像C语言一样,C++语言中也没有输入输出语句。**但C++标准库中有一个面向对象的输入输出软件包,它就是I/O流类库。**程序建立一个流对象,并指定这个流对象与某个文件对象建立连接,程序操作流对象,流对象通过文件系统对所连接的文件对象产生作用。I/O流类库的基础是一组类模板,类模板提供了库中的大多数功能,而且可以作用于不同类型的元素。

P482:在I/O流类库中,头文件iostream声明了4个预定义的流对象用来完成在标准设备上的输入输出操作:cin,cout,cerr,clog。

第十二章 异常处理

P512:C++语言中提供对处理异常情况的内部支持。try、throw、catch语句就是C++语言中用于实现异常处理的机制。

#include 
using namespace std;
int divide(int x,int y) {
    if(y == 0) {
        throw x;
    }
    return x/y;
}
int main() {
    try {
        cout << "5/2=" << divide(5,2) << endl;
        cout << "9/0=" << divide(9,0) << endl;

    } catch (int e) {
        cout << e << " is divided by zero!" << endl;
    }
    cout << "That is ok." << endl;
    return 0;
}

P515:可以在函数的声明中列出这个函数可能抛出的所有异常类型。如:

void fun() throw(A,B,C,D);

一个不抛出任何异常类型的函数可以做如下声明:

void fun() throw();

P516:C++异常处理的真正功能,不仅在于它能够处理各种不同类型的异常,还在于它具有为异常抛掷前构造的所有局部对象自动调用析构函数的能力

网摘

函数指针与指针函数

摘自:https://blog.csdn.net/qq_25652139/article/details/80831278

函数指针:一种特殊的指针,它指向函数的入口

//定义一个函数指针,只能指向返回值为int,参数为两个int的函数
int (*p) (int,int);

指针函数:指的是返回值是指针的函数

//这是一个参数为2个int,返回值是int型指针的函数
int *p (int,int);

C++ 多线程

摘自:https://www.runoob.com/cplusplus/cpp-multithreading.html

POSIX Threads 或 Pthreads 提供的 API 可在多种类 Unix POSIX 系统上可用,比如 FreeBSD、NetBSD、GNU/Linux、Mac OS X 和 Solaris。

创建线程:pthread_create创建一个新的线程,并让它可执行。

#include 
pthread_create(thread,attr,start_routine,arg)
参数 描述
thread 指向线程标识符指针
attr 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。
start_routine 线程运行函数起始地址,一旦线程被创建就会执行。
arg 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。

创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。

终止线程

使用下面的程序,我们可以用它来终止一个 POSIX 线程:

#include 
pthread_exit (status) 

在这里,pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。

如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。

#include 
#include 
using namespace std;

#define NUM_THREADS 5

void* say_hello(void* threadid) {
    int tid = *((int*)threadid);
    cout << "Hello:"<

连接和分离线程

我们可以使用以下两个函数来连接或分离线程:

pthread_join (threadid, status) 
pthread_detach (threadid) 

pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连接

你可能感兴趣的:(读书笔记)