c++总结

面向对象程序设计

面向对象和面向过程的设计的对比

项目名称 面向对象 面向过程(结构化设计)
定义 把整个需求按照特点、功能划分,将这些存在共性的部分封装成类(类实例化后才是对象),创建了对象不是为了完成某一个步骤,而是描述某个事物在解决问题的步骤中的行为 分析出解决问题所需要的步骤,然后用函数一步步的实现,实用的时候,一个个的一次调用即可
特点 封装、继承、多态 算法+数据结构
优势 适用于大型复杂系统,方便复用 适用于简单系统,容易理解
劣势 比较抽象 难以应对复杂系统,难以复用
设计语言 java,c++,c#,python等 c

例如:五子棋的设计。面向过程是首先分析问题的步骤:1.开始游戏 2.黑子先走 3.绘制图画等等,最后输出结果,将对应的步骤分函数实现即可。

面向对象是:整个五子棋可以分为:1.黑白双方,行为是一样的 2.棋盘系统,负责绘制画面 3.规则系统,负责判断犯规,输赢等。玩家对象负责接收用户输入,并告知棋盘对象棋子的布局的变化,棋盘对象接收到了棋子的变化负责在屏幕上显示这种变化,同时用规则系统判定。

面向对象的程序设计的基本特征

封装

将有关的数据和操作代码封装在一个对象中,形成一个基本单位,各个对象之间相互独立,互不干扰。用户对不同的数据有不同的权限,既降低了操作对象的复杂程度,又使安全性大大增加。

意义:

  • 将属性和行为作为整体,表现生活中的事物
  • 将属性和行为加以权限控制

语法:

1
2
3
4
class 类名
{
访问权限:属性/行为
};

继承

描述类之间的关系。子类具有父类(基类)的部分特性,又可以增加自己的新特征,还可以继承父类的protected和public类型的数据。

如果子类中出现与父类同名的成员函数,子类的同名成员会隐藏掉父类的,可以加作用域符号访问父类的成员。

好处:

  • 可以减少重复代码
  • 从基类继承表现共性,自定义的新增成员表现个性

继承的权限

public继承

  • 父类中的公共权限到子类还是公共的
  • 父类中的保护权限到子类中还是保护的
  • 父类的私有成员子类访问不到

protected继承

  • 父类的公共子类变保护
  • 父类的保护到子类变保护
  • 父类的私有成员子类访问不到

private继承

  • 父类的公共成员变私有
  • 父类的保护成员变私有
  • 父类的私有成员子类访问不到

父类中所有的非静态成员都会被子类继承下去,私有成员也继承了但是被编译器隐藏了,访问不到

多继承

多继承中如果父类出现了同名情况,子类使用的时候要加作用域。

经典的有菱形继承:两个派生类继承同一个基类,又有一个类用时继承这两个派生类。

两个父类有相同的成员名时,需要加作用域区分,但菱形继承导致数据有两份,资源浪费,可以利用虚继承解决。继承之前加virtual变为虚继承,此类称为虚基类。

继承的构造与析构顺序

先构造父类,再构造子类,析构的顺序与构造相反。

多态性

多态性是指用一个名字定义不同的函数,这些函数执行不同但又类似的操作,这样就可以用同一个函数名调用不同内容的函数。

静态多态和动态多态的区别

  • 静态多态编译阶段确定函数地址
  • 动态多态运行时确定函数地址

静态多态(函数重载、运算符重载)

运算符重载

基本规则

  • 重载后运算符的优先级和结合性不会改变
  • 不能改变原运算符操作数的个数
  • 不能重载c++中没有的运算符
  • 不能改变运算符的原有语义
  • 运算符重载函数的参数至少应有一个是类对象(或类对象的引用)
  • 重载函数可以是普通函数,类的成员函数或类的友元函数

重载的两种方式

  • 重载为类的友元函数
    • 双目运算符、具有交换性的运算符、输入输出、左边的操作数不是类的类型。
  • 重载为类的成员函数
    • 单目运算符、需要修改类的数据,重载运算符的关键字是operator。输入输出运算符的重载必须为友元函数,且必须有输入输出流的返回值。

输入输出

1
2
3
4
5
6
friend istream& operator>>(istream &in, 重载类名 &s)
{

return in;
}
friend ostream& operator<<(ostream&,重载类名&);

动态多态(派生类和虚函数实现运行时多态)

满足的条件:

  • 有继承关系
  • 子类重写父类的虚函数

使用:

  • 父类的指针或者引用,执行子类对象

c++概述

结构体、联合体和枚举名可以直接作为类型名

在定义变量时,前面不用加对应的关键字(struct、union、enum)

const

三种情况:

  • 常量指针

    const int *p=&a;
    指针的指向可以修改,但是指针执行的值不可以改。

  • 指针常量
    int *const p=&a;
    指针的指向不能改,指向的值可以改

  • 修饰常量
    常量的值不能改

全局变量和局部变量

局部变量在作用域内权限高于全局变量。可以用作用域运算符“::”访问全局变量。如“::a”,表示全局变量a。

全局变量可以一直存在,存放在全局区,局部变量使用后不保存。

static作用

  1. 全局静态变量

在全局变量前加上关键字static,全局变量就定义成一个全局静态变量。

静态存储区,在整个程序运行期间一直存在。

初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化)。

作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。

  1. 局部静态变量

在局部变量之前加上关键字static,局部变量就成为一个局部静态变量。

内存中的位置:静态存储区。

初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化)。

作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变。

  1. 静态函数

在函数返回类型前加static,函数就定义为静态函数。函数的定义和声明在默认情况下都是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。

函数的实现使用static修饰,那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突。

warning:不要再头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则cpp内部声明需加上static修饰。

  1. 类的静态成员

在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员。对多个对象来说,静态数据成员只存储一处,供所有对象共用。

  1. 类的静态函数

静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。

在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员(这点非常重要)。如果静态成员函数中要引用非静态成员时,可通过对象来引用。从中可看出,调用静态成员函数使用如下格式:<类名>::<静态成员函数名>(<参数表>);

内联函数(空间换时间)

函数前加inline。消除函数调用时的系统开销,提高运行速度。

对此函数调用是直接将代码插入到调用出,同时实参取代形参。

与宏定义的区别

宏定义是预编译程序对齐预处理的,它只做简单的字符置换,但是不做语法检查。使用内联函数可以保存宏定义的优点,同时不会出现副作用。

函数重载

函数名可以相同,提高复用性。

使用条件:

  • 同一个作用域下
  • 函数名称相同
  • 函数的参数类型不同或者个数不同或者顺序不同

注意事项:

  • 函数返回值不在参数匹配检查中,因此只有返回值类型不同,则不允许重载。
  • 函数重载与带默认值的函数一起实用时,有可能引起二义性
  • 函数调用时,如果给出的实参和形参类型不相符,编译器会自动做类型转换,可能导致不可识别的错误。如定义了int和long,但是传入的是float类型

union联合体(无名联合)

用的少,一般与结构体比较,具体可百度。

  • 结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。

  • 结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。

  • 共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。

new和delete

  • new可以自动计算所要分配的内存的大小
  • new能够自动返回正确的指针类型,不用像malloc一样强制转换
  • new int [10],分配数组大小
  • new int(初值) 分配内存的同时初始化
  • new分配失败时一般返回NULL
  • delete删除数组delete []指针变量名;
  • new分配的空间不会自动释放,需要用delete

引用

引用的本质就是一个指针常量

注意事项

  • 引用必须初始化,初始化后不能更改
  • 引用可以作为函数的返回值存在的
  • 不要返回局部变量的引用
  • 可以在函数调用时作为左值
  • 引用类型和被引用的类型要一致

类和对象

基本概念

  • 对象:是描述其属性的数据及对这些数据的操作封装的统一体。把数据和操作看成一个整体。

  • 方法:将函数定义为类的一部分或者将函数与某个对象绑定,该函数就称之为方法。

  • 类:是实现信息封装的基础,类包含数据说明和一组操作数据的方法。类的实例是对象。 (从同类对象中抽出共性形成类。)

类是对象的抽象,对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。

类相当于自己定义的一个新的类型,可以用来定义对象,其地位和int等的地位相同。类定义的对象的大小取决于对象数据成员所占大小

实例化就是通过类创建对象的过程

类的成员函数定义和声明

类是抽象的数据类型,不占用存储空间,因此声明中不能给数据成员赋值。

函数写在声明内时,默认定义为内联成员函数。一般类内只声明,类外实现。

类外实现用:类名::函数,只有::无类名或者都无,则是普通函数。

访问权限

  • public:类内、外都可以访问
  • protected: 内可以,外不可以,子类可以访问父类的保护内容
  • private: 内可以,外不可以,子类不能访问父类的私有内容

struct和class的区别

  • struct默认权限为公
  • class默认为私

构造和析构函数

  • 构造函数和类名相同,不能指定返回值类型可以重载

  • 作用是在定义对象时对数据进行初始化,建立对象时自动调用,不能被用户调用。一个类只能由一个默认构造函数,类中定义了全部是默认参数的构造函数后就不能再定义重载构造函数。

  • 析构函数是在类名前加一个~,作用是在撤销对象占用的内存之前完成一些清理工作,而非删除对象。与构造函数类似的是析构函数不返回任何值,没有函数类型,不同的是析构函数不能被重载,没有函数参数。一个类可以有多个构造函数,却只能由一个析构函数,在撤消对象占用内存前调用。

析构的顺序是:先被定义的后析构。

其他类作为本类成员,构造时,先构造其他类的对象,再构造自身的。

拷贝构造函数

调用有三种情况:

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传值
  • 以值方式返回局部对象

匿名对象:Person(10);执行后系统自动清理,不要利用拷贝构造函数初始化匿名对象。

深浅拷贝

  • 浅拷贝:简单的赋值。堆区内容容易重复释放
  • 深拷贝:在堆区重新申请空间,进行拷贝操作

用深拷贝解决指针悬挂问题。

静态成员

成员变量或者函数前加static。不属于某一对象,实例化对象时不为静态成员分配空间,静态数据成员是在定义时被分配空间,到程序结束时才释放空间。可以被初始化,但必须要在类外,其关键字为static。静态成员函数没有this指针,不能访问非静态数据成员,用于访问静态数据成员。

静态成员变量:

  • 所有对象共享同一份数据
  • 在编译阶段分配内存
  • 类内声明,类外初始化

静态成员函数:

  • 所有对象共享同一个函数
  • 静态成员函数只能访问静态成员变量

成员变量和成员函数分开存储

只有非静态成员变量才属于类的对象上吗,非静态成员函数不属于类的对象上。

空对象的内存空间大小为1

每个空对象也分配一个字节空间,是为了区分空对象占内存的位置,每个空对象也应该有一个独一无二的内存地址。

this指针

this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。

是隐含每一个非静态成员函数内的一种指针,不需要定义,直接使用。本质是指针常量。

用途:

  • 当形参和成员变量同名时,可以用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this

当前类的对象。调用自己的构造函数

常类型

常数据成员:只能通过构造函数的初始化表对常数据成员进行初始化

常成员函数:只能引用本类的数据成员,但不能修改

常对象:定义时必须同时进行初始化,且之后不能再改变,只能调用常成员函数,只能被常指针指向。

常函数

  • 成员函数后加const称为常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明时加关键字mutable,在常函数中依然可以修改

常对象

  • 声明对象前加const称为常对象
  • 常对象只能调用常函数
  • 常对象的数据成员值在对象的整个生存期内不能被改变

友元函数(可访问对应的私有成员)

三种实现:

  • 全局函数做友元
  • 类做友元
  • 成员函数做友元

在一个类的声明中加如另一个类,声明为friend,则那个类可以访问本类的私有成员,同理另一个类的成员函数可单独声明作为友元函数。

虚基类

如果一个类有多个直接基类,这些直接基类又有共同的基类,则派生类中会保留这个间接的共同基类数据成员的多份同名成员,造成内存浪费,经典的有菱形继承。在访问这些同名的成员时,必须在派生类对象名后增加直接基类名,使其唯一的标识一个成员,以免产生二义性。

使公共基类只产生一个复制,则可以将基类说明为虚基类。

虚基类在派生类中声明:

1
2
3
4
class 派生类名:virtual 继承方式 基类名
{

}

虚基类的初始化

  • 虚基类定义带形参的构造函数,并且没有定义默认形式的构造函数,则整个继承结构中,所有直接或间接的派生类都必须在构造函数的成员初始化表中列出对虚基类构造函数的调动,以初始化在虚基类中的数据成员
  • 建立一个对象时,对象含有从虚基类继承来的成员,则虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的。该派生类的其他基类对虚基类构造函数的调用都自动被忽略。
  • 若同一层次中同时包含虚基类和非虚基类,应先调用虚基类的构造函数,再调用非虚基类的构造函数,最后调用派生类构造函数。
  • 对于多个虚基类,构造函数的执行顺序仍然是先左后右,自上而下
  • 对于非虚基类,构造函数的执行顺序仍然是先左后右,自上而下
  • 若虚基类是非虚基类派生而来,则仍然先调用基类构造函数,再调用派生类的构造函数。

运算符重载(编译时)

重载的部分注意事项在第一章介绍多态的部分

加法重载

示例:

1
2
3
4
5
Complex operator+(Complex a1,Complex a2)
{
...
}
total=a1+a2;//显示调用/隐式调用,等价于total=operator+(a1,a2)

不能运算法重载的

  • . //成员访问运算符
  • .*成员指针访问运算符
  • :: 作用域运算符
  • suzeif长度运算符
  • ?:条件运算符

不能定义为友元运算符重载函数的

=、[]、函数调用“()”

双目运算符重载

成员运算符重载函数的形参表仅有一个参数,作为运算符的右操作数。通过this指针隐含传递的。

  • aa@bb //隐式调用
  • aa.operator@(bb)//显式调用

成员运算符重载函数和友元运算符重载函数的比较

虚函数

虚函数是重载的一种表现形式。是动态的重载,运行时的多态,就是在运行时才决定如何动态,即动态联编。

基类的对象指针可以指向它的公有派生的对象,但是当其指向公有派生类对象时,它只能访问派生类中从基类继承来的成员,而不能访问公有派生类中定义的成员。

而引入虚函数,程序在运行时根据指针所指向的实际对象,调用该对象的成员函数,可以解决此类问题。

虚析构函数

c++不能声明虚构造函数,但是可以声明虚析构函数。当派生类对象撤销时,一般先调用派生类的析构函数,然后再调用基类的析构函数。

但是遇到指针会先撤销基类的,因此要将基类的析构函数声明为虚析构函数。

纯虚函数

纯虚函数是在基类中的虚函数,在该基类中没有定义,但派生类需要定义,或仍然说明为纯虚函数。

用法:

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

作用:

在基类中为派生类保留一个函数的名字,以便派生类根据需要对它重新定义。

类中含有纯虚函数,则成为抽象类。

抽象类的使用规定:

  • 抽象类只能用作其他类的基类,不能建立抽象类对象
  • 抽象类不能用作参数类型、函数返回类型或显式转换的类型。但可以声明指向抽象类的指针变量,此指针可以指向它的派生类,进而实现多态性
  • 如果抽象类的派生类没有重新说明纯虚函数,则依然是抽象类

模版与异常处理

函数模版

1
2
3
4
5
6
7
8
9
10
11
12
template <typename 类型参数>
返回类型 函数名(模版形参表)
{
函数体
}
//也可以用下面这种
template <class 类型参数>
返回类型 函数名(模版形参表)
{
函数体
}
//对应的参数使用时必须要实例化

注意:

  • 允许多个类型参数,参数必须加关键字typename或class
  • template与函数模版定义之间不能有别的语句
  • 模版函数类似于重载函数,只不过更严格一些。同一模版函数实例化后执行相同的操作。
  • 函数模版也可以重载
  • 函数模版与同名的非模版函数也可以重载,先找的是非模版函数,找不到再看模版函数。

类模版

定义与函数模版类似

使用注意:

  • 需要在成员函数定义前进行模版声明
  • 在成员函数名前缀上“类名<类型参数>::”

类模版外定义的成员函数的一般形式:

1
2
3
4
5
template<typename 类型参数>
函数类型 类名<类型参数>::成员函数名(形参表)
{
...
}

异常处理

程序运行时出现的错误称为异常。处理异常机制:try(检查)、throw(抛出)、catch(捕获)

使用:

1
2
3
4
5
6
7
8
9
10
11
12
try
{
被检查的语句
}
catch(异常类型声明1)
{
进行异常处理的复合语句1
}
catch(...)//表示异常是任意类型
{
进行异常处理的复合语句2
}

流类库与输入输出

示例:

1
2
3
4
5
6
7
8
#include<fstream>
using namespace std;
int main()
{
ofstream OF("test.txt");
OF<<"hello";
return 0;
}

遇过的简答

重写和重载的区别

重载实现的是编译时的多态性,重写实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型。

定义方法:

1
2
3
4
virtual 函数类型 函数名(形参表)
{
函数体
}

在基类中的成员函数被声明为虚函数,此函数可以在派生类中被重新定义。重新定义时,函数原型必须相同,即类型、名称、参数个数和顺序必须相同。

注意:

  • 在类内声明虚函数,类外定义时不要加virtual
  • 虚函数无论被公有继承多少次,仍保持虚函数的特性
  • 虚函数必须是所在类的成员函数,不能是友元函数,也不能是静态函数

c++比c特别的地方

设计思想上:

C++是面向对象的语言,而C是面向过程的结构化编程语言

语法上:

C++具有封装、继承和多态三种特性

C++相比C,增加多许多类型安全的功能,比如强制类型转换、

C++支持范式编程,比如模板类、函数模板等

C/C++ 中指针和引用的区别

1.指针有自己的一块空间,而引用只是一个别名;

2.使用sizeof看一个指针的大小是4,而引用则是被引用对象的大小;

3.指针可以被初始化为NULL,而引用必须被初始化且必须是一个已有对象的引用;

4.作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对引用的修改都会改变引用所指向的对象;

5.可以有const指针,但是没有const引用;

6.指针在使用中可以指向其它对象,但是引用只能是一个对象的引用,不能 被改变;

7.指针可以有多级指针(**p),而引用至于一级;

8.指针和引用使用++运算符的意义不一样;

9.如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄露。

为什么析构函数必须是虚函数?为什么C++默认的析构函数不是虚函数

可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。

C++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此C++默认的析构函数不是虚函数,而是只有当需要当作父类时,设置为虚函数。

函数指针

函数指针是指向函数的指针变量。C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的。

用途:调用函数和做函数的参数,比如回调函数。

new/delete与malloc/free的区别是什么

首先,new/delete是C++的关键字,而malloc/free是C语言的库函数,后者使用必须指明申请内存空间的大小,对于类类型的对象,后者不会调用构造函数和析构函数

C语言是怎么进行函数调用的?

每一个函数调用都会分配函数栈,在栈内进行函数执行过程。调用前,先把返回地址压栈,然后把当前函数的esp指针压栈。

C++如何处理返回值?

生成一个临时变量,把它的引用作为函数参数传入函数内。

fork,wait,exec函数

父进程产生子进程使用fork拷贝出来一个父进程的副本,此时只拷贝了父进程的页表,两个进程都读同一块内存,当有进程写的时候使用写实拷贝机制分配内存,exec函数可以加载一个elf文件去替换父进程,从此父进程和子进程就可以运行不同的程序了。fork从父进程返回子进程的pid,从子进程返回0.调用了wait的父进程将会发生阻塞,直到有子进程状态改变,执行成功返回0,错误返回-1。exec执行成功则子进程从新的程序开始运行,无返回值,执行失败返回-1

map和set有什么区别,分别又是怎么实现的?

map和set都是C++的关联容器,其底层实现都是红黑树(RB-Tree)。由于 map 和set所开放的各种操作接口,RB-tree 也都提供了,所以几乎所有的 map 和set的操作行为,都只是转调 RB-tree 的操作行为。

map和set区别在于:

(1)map中的元素是key-value(关键字—值)对:关键字起到索引的作用,值则表示与索引相关联的数据;Set与之相对就是关键字的简单集合,set中每个元素只包含一个关键字。

(2)set的迭代器是const的,不允许修改元素的值;map允许修改value,但不允许修改key。其原因是因为map和set是根据关键字排序来保证其有序性的,如果允许修改key的话,那么首先需要删除该键,然后调节平衡,再插入修改后的键值,调节平衡,如此一来,严重破坏了map和set的结构,导致iterator失效,不知道应该指向改变前的位置,还是指向改变后的位置。所以STL中将set的迭代器设置成const,不允许修改迭代器的值;而map的迭代器则不允许修改key值,允许修改value值。

(3)map支持下标操作,set不支持下标操作。map可以用key做下标,map的下标运算符[ ]将关键码作为下标去执行查找,如果关键码不存在,则插入一个具有该关键码和mapped_type类型默认值的元素至map中,因此下标运算符[ ]在map应用中需要慎用,const_map不能用,只希望确定某一个关键值是否存在而不希望插入元素时也不应该使用,mapped_type类型没有默认值也不应该使用。如果find能解决需要,尽可能用find。

STL有什么基本组成

STL主要由:以下几部分组成:
容器迭代器仿函数算法分配器配接器
他们之间的关系:分配器给容器分配存储空间,算法通过迭代器获取容器中的内容,仿函数可以协助算法完成各种操作,配接器用来套接适配仿函数

vector和list的区别

List:动态链表,在堆上分配空间,每插入一个元数都会分配空间,每删除一个元素都会释放空间。

区别:

1)vector底层实现是数组;list是双向 链表。

2)vector支持随机访问,list不支持。

3)vector是顺序内存,list不是。

4)vector在中间节点进行插入删除会导致内存拷贝,list不会。

5)vector一次性分配好内存,不够时才进行2倍扩容;list每次插入新节点都会进行内存申请。

6)vector随机访问性能好,插入删除性能差;list随机访问性能差,插入删除性能好。

3、应用

vector拥有一段连续的内存空间,因此支持随机访问,如果需要高效的随即访问,而不在乎插入和删除的效率,使用vector。

list拥有一段不连续的内存空间,如果需要高效的插入和删除,而不关心随机访问,则应使用list。

开发方面

多线程

每个进程都可以有一个主线程,并且只有一个主线程,实际上是主线程调用main函数的代码,线程可以为一个运行代码的通道,路径,我们可以自己创建多个线程。

为什么需要多线程,因为我们希望能够把一个任务分解成很多小的部分,各个小部分能够同时执行,而不是只能顺序的执行,以达到节省时间的目的。对于求和,把所有数据一起相加和分段求和再相加没什么区别。

一个线程只能join一次,多次会引发中断。

join

主线程和子线程之间是同步的关系,即主线程要等待子线程执行完毕才会继续向下执行,join()是一个阻塞函数

detach

表示主线程不用等待子线程执行完毕,两者脱离关系.

joinable

判断当前线程是否可以做join或者detach过程。

创建线程的方法

  • 普通函数
  • 通过类和对象
  • Lambda表达式创建线程
  • 带参的方式创建线程
  • 带智能指针创建线程
  • 通过类的成员函数创建线程
文章目录
  1. 1. 面向对象程序设计
    1. 1.1. 面向对象和面向过程的设计的对比
    2. 1.2. 面向对象的程序设计的基本特征
      1. 1.2.1. 封装
      2. 1.2.2. 继承
        1. 1.2.2.1. 继承的权限
        2. 1.2.2.2. 多继承
        3. 1.2.2.3. 继承的构造与析构顺序
      3. 1.2.3. 多态性
        1. 1.2.3.1. 静态多态和动态多态的区别
        2. 1.2.3.2. 静态多态(函数重载、运算符重载)
          1. 1.2.3.2.1. 运算符重载
        3. 1.2.3.3. 动态多态(派生类和虚函数实现运行时多态)
  2. 2. c++概述
    1. 2.1. 结构体、联合体和枚举名可以直接作为类型名
    2. 2.2. const
    3. 2.3. 全局变量和局部变量
    4. 2.4. static作用
    5. 2.5. 内联函数(空间换时间)
      1. 2.5.1. 与宏定义的区别
    6. 2.6. 函数重载
    7. 2.7. union联合体(无名联合)
    8. 2.8. new和delete
    9. 2.9. 引用
  3. 3. 类和对象
    1. 3.1. 基本概念
    2. 3.2. 类的成员函数定义和声明
    3. 3.3. 访问权限
    4. 3.4. struct和class的区别
    5. 3.5. 构造和析构函数
    6. 3.6. 拷贝构造函数
    7. 3.7. 深浅拷贝
    8. 3.8. 静态成员
    9. 3.9. 成员变量和成员函数分开存储
    10. 3.10. 空对象的内存空间大小为1
    11. 3.11. this指针
    12. 3.12. 常类型
      1. 3.12.1. 常函数
      2. 3.12.2. 常对象
    13. 3.13. 友元函数(可访问对应的私有成员)
    14. 3.14. 虚基类
      1. 3.14.1. 虚基类的初始化
    15. 3.15. 运算符重载(编译时)
      1. 3.15.1. 加法重载
      2. 3.15.2. 不能运算法重载的
      3. 3.15.3. 不能定义为友元运算符重载函数的
      4. 3.15.4. 双目运算符重载
      5. 3.15.5. 成员运算符重载函数和友元运算符重载函数的比较
    16. 3.16. 虚函数
    17. 3.17. 虚析构函数
    18. 3.18. 纯虚函数
  4. 4. 模版与异常处理
    1. 4.1. 函数模版
    2. 4.2. 类模版
    3. 4.3. 异常处理
  5. 5. 流类库与输入输出
  6. 6. 遇过的简答
    1. 6.1. 重写和重载的区别
    2. 6.2. c++比c特别的地方
    3. 6.3. C/C++ 中指针和引用的区别
    4. 6.4. 为什么析构函数必须是虚函数?为什么C++默认的析构函数不是虚函数
    5. 6.5. 函数指针
    6. 6.6. new/delete与malloc/free的区别是什么
    7. 6.7. C语言是怎么进行函数调用的?
    8. 6.8. C++如何处理返回值?
    9. 6.9. fork,wait,exec函数
    10. 6.10. map和set有什么区别,分别又是怎么实现的?
    11. 6.11. STL有什么基本组成
    12. 6.12. vector和list的区别
  7. 7. 开发方面
    1. 7.1. 多线程
      1. 7.1.1. join
        1. 7.1.1.1. detach
      2. 7.1.2. joinable
      3. 7.1.3. 创建线程的方法