C++知识点学习——01

1、智能指针:

智能指针是帮助程序员管理动态分配资源的一种工具,主要就是防止程序员忘记释放堆上资源导致的内存泄漏,本质是一个类模板,可以动态的分配任何类型的资源,在析构函数中在合适的时机释放该资源。

具体分为unique_ptr(独占指针或者unique pointer),shared_ptr(共享指针,shared pointer)和weak_ptr

独占指针就是同一时刻只能有一个指针指向动态分配的资源,内部禁止拷贝和赋值,但有移动构造函数

相反的,共享指针就是同一时刻可以有多个指针指向同一个动态分配的资源,内部通过一个引用计数表示该资源被多少个指针指向。当一个共享指针离开作用域,会将引用计数减一,引用计数为0才真正释放资源。

为了解决共享指针的循环引用问题 引入了Weak pointer,(不要主动展开循环引用,真的很难用文字表达!) 一个弱指针指向一个共享的对象时不会增加引用计数。

可参考如下链接:

https://blog.csdn.net/sinat_31608641/article/details/107702175

2、vector和list

vector和list都是一个用来存放相同元素的集合

不同点就 vector内存中连续存放,随机访问,插入删除复杂度高

List不连续,链表形式存放,插入删除复杂低。

3、new和malloc区别

可参考:new和malloc区别

new/delete是C++的操作符,而malloc/free是库函数;

new在调用时会先为对象分配内存,再调用对象的构造函数,而malloc不会;

见下列实例:使用new会调用类A的构造函数,使用delete会调用A的析构函数;而malloc/free并不会。

class A
{
public:
    A() :val(666) 
    {
        cout << "这是构造函数" << endl;
    }
    ~A()
    {
        cout << "这是析构函数" << endl;
    }
public:
    int val;
};
 
int main(int argc, char *argv[])
{
    cout << "new的使用:" << endl;
    A* a = new A();
    cout << "a的数值:" << a->val << endl;
    delete a;
 
    cout << endl << endl << "malloc的使用:" << endl;
    A* aa = (A*)malloc(sizeof(A));
    cout << "aa的数值:" << aa->val << endl;
    free(aa);
 
    system("pause");
    return 0;
}
  • 使用malloc为对象指针分配内存,要明确指定分配内存的大小,而new不需要
  • new作为操作符,可以被重载,而malloc不行

new操作符的重载如下所示

class A
{
public:
    A() 
    {
        cout << "这是构造函数" << endl;
    }
    ~A()
    {
        cout << "这是析构函数" << endl;
    }
 
    void* operator new(std::size_t size)
    {
        cout << "重载new运算符。对象size:" << size << endl;
        return malloc(size);
    }
public:
    int val;
};
 
int main(int argc, char *argv[])
{
    cout << "new的使用:" << endl;
    A* a = new A();
    delete a;
 
    system("pause");
    return 0;
}

可见,重载的操作符new在被调用后,也会调用构造函数。

  • new分配内存成功,则返回对象指针;而malloc分配成功会返回void*类型指针

从上面的实例中,我们就可以注意到malloc返回的指针还需要进行强制类型转换才赋值给A*的指针对象,而使用new则不需要,直接返回的是A*的指针。

使用new是类型安全的。

int main(int argc, char *argv[])
{
    cout << "new的使用:" << endl;
    A* a = new A();
    cout << "a的数值:" << a->val << endl;
    delete a;
 
    cout << endl << endl << "malloc的使用:" << endl;
    A* aa = (A*)malloc(sizeof(A));
    cout << "aa的数值:" << aa->val << endl;
    free(aa);
 
    system("pause");
    return 0;
}
  •  new如果分配内存失败会抛出bad_alloc异常;而malloc分配内存失败会返回null指针

准确的说,C++标准中new如果分配内存失败要求抛出bad_alloc异常,但是有些编译器对C++标准的适配没那么好,也会返回null指针,这是保留了C的处理。而malloc分配失败则会返回null指针。

  • new从自由存储区为对象分配内存;malloc从堆上分配内存

我们知道内存分为:代码区、常量存储区、静态存储区(全局存储区)、堆区、栈区(关于内存分区可以见下面的文章)。自由存储区又是在什么位置呢?

事实上,自由存储区是C++为new操作符抽象出的概念,使用new分配出的区域就是自由存储区。而在物理意义的内存上,自由存储区可以在堆也可以在其他地方(比如静态存储区),这取决于编译器从哪里为new的使用分配内存。

基本上,C++编译器默认在堆区为new开辟自由存储区。

4、C++中的多态

可参考:多态的解释 

可参考:C++中的多态

多态的概念:多态就是函数调用的多种形态,使用多态能够使得不同的对象去完成同一件事时,产生不同的动作和结果。


多态又分为静态多态和动态多态:
(1)静态多态,也称为静态绑定或前期绑定(早绑定):函数重载和函数模板实例化出多个函数(本质也是函数重载)。静态多态也称为编译期间的多态,编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误。

(2)动态多态,也称为动态绑定或后期绑定(晚绑定):在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,即运行时的多态。在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。

父类指针或引用指向父类,调用的就是父类的虚函数
父类指针或引用指向子类,调用的就是子类的虚函数


为什么动态多态无法在编译时就确定:在编译的时候编译器并不知道用户选择的是哪种类型的对象。如果不是虚函数,则采用静态绑定,函数体与函数调用在程序运行之前(编译期间)就绑定了。
当函数声明为虚函数时,如果使用指针或引用的形式,那么由于指针指向的对象不确定是基类还是派生类,那么编译器就无法采用静态绑定,所以就只有通过动态绑定的方式。因此编译器通过创建一个虚函数表存放虚函数的地址,在运行时,编译器通过虚函数指针在虚函数表中找到正确的函数版本,然后调用。


总结起来就是:不同的类型对象,去完成同一件事情,产生的动作是不一样的。
比如抢红包的动作是一样的,但是每个人抢到的金额的大小是不同的,或者买票的时候,做的是同一个事情(买票)、但是不同的人去买票的价格是不一样的、普通乘客去买票是全价、学生去买票是半价,这些就是所谓的多态。

多态的构成条件:
多态是指不同继承关系的类对象,去调用同一函数,产生了不同的行为。在继承中要想构成多态需要满足两个条件:

必须通过基类的指针或者引用调用虚函数。
被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。
 

5、指针和引用

可参考:指针和引用的区别

可参考:指针和引用的区别

 指针和引用主要有以下区别:

  • 引用必须被初始化,但是不分配存储空间。指针不声明时初始化,在初始化的时候需要分配存储空间。
  • 引用初始化后不能被改变,指针可以改变所指的对象。
  • 不存在指向空值的引用,但是存在指向空值的指针

注意:引用作为函数参数时,会引发一定的问题,因为让引用作参数,目的就是想改变这个引用所指向地址的内容,而函数调用时传入的是实参,看不出函数的参数是正常变量,还是引用,因此可能引发错误。所以使用时一定要小心谨慎。

从概念上讲。指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。

而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量)。

在C++中,指针和引用经常用于函数的参数传递,然而,指针传递参数和引用传递参数是有本质上的不同的:

  • 指针传递参数本质上是 值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在中开辟了内存空间以存放由主调函数放进来的 实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。
  • 而在引用传递过程中, 被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间 接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
  • 引用传递和指针传递是 不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针 传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量,那就得使用指向指针的 指针,或者指针引用。

从编译的角度来阐述它们之间的区别:

程序在编译时分别将指 针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为 引用对象的地址值。符号表生成后就不会再改,因此指针可以改变其指向的对象(指针变量中的值可以改),而引用对象则不能修改。

最后,总结一下指针和引用的相同点和不同点:

★相同点:

●都是地址的概念;

指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。

★不同点:

●指针是一个实体,而引用仅是个别名;

●引用只能在定义时被初始化一次,之后不可变;指针可变;引用“从一而终”,指针可以“见异思迁”;

引用没有const,指针有const,const的指针不可变

●引用不能为空,指针可以为空

●“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小(64位操作系统是8,32位操作系统是4);

●指针和引用的自增(++)运算意义不一样;

●引用是类型安全的,而指针不是 (引用比指针多了类型检查)

6、static关键字

可参考(写得好棒):C++中的static关键字

静态全局变量:   将全局变量隐藏, 在外部文件不能访问该全局变量

静态局部变量:   将局部变量存储在 静态存储区, 生命周期 扩大为整个程序的生命周期

静态成员变量:   该成员变量为类中所有对象共有,类外初始化,全局唯一, 不需要实例化即可访问

静态成员方法:   只能访问静态成员变量,没有this指针

7、内存分区

可参考:C++中的内存分区

代码区:存放 函数体的二进制代码 ,由操作 系统管理
全局区:存放 全局变量 和 静态变量 以及 常量
栈  区:  由 编译器自动分配释放,存放函数的参数值、局部变量等
堆  区:  由 程序员分配和释放,若程序员不释放,程序结束时由操作系统回收


不同区域存放的数据,赋予不同的生命周期

程序运行前:
程序编译后,生成了 exe 可执行程序,未执行该程序前分为两个区域(代码区和全局区)

代码区:

存放 cpu 执行的机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
代码区是只读的,使其只读的原因是防止程序意外地修改他的指令


全局区:

存放全局变量和静态变量
全局区还包含常量区,字符串常量和其他常量也存放在此
该区的数据在程序结束后由操作系统释放

程序执行时:

栈区:

编译器自动分配释放,存放 函数的参数值局部变量 

注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

堆区:

由程序员分配释放,若程序员不释放,程序结束是由操作系统回收

在 C++ 中主要用 new 在堆区开辟内存

 利用 new 关键字,可以将数据开辟到堆区,new 返回内存编码。需要delete释放空间, 释放数组时,delete [] 指针; // 释放数组的时候要加  [ ] (方括号),不加 [ ]  则只会释放一个指针。

function 中的指针本质也是局部变量,放在 栈区,指针保存的数据放在堆区。

8、静态链接,动态链接

可参考:静态链接与动态链接

(通俗来讲:

编译的四个步骤 预处理,编译,汇编,链接。最后一步就是二进制库文件放进来。使用静态链接得先生成静态库,将一个cpp文件生成,里面一般就一些函数。静态链接就把所有函数全放到可执行文件中,所以可执行文件大一些。

动态链接就是你生成库之后,可指向文件中记录下动态库函数的信息,运行可执行文件后,再从动态库中加载库函数,其实就是一行行代码,动态库一般加载在堆区和栈区中间好像。

适用场景就说说优缺点,更新一个静态库后,还得重新链接生成可执行文件。动态链接更新了一般就不用。 但是动态链接因为要加载,所有运行会慢。静态链接体积大,浪费内存。同样的代码,会在很多可执行文件中存在

静态链接和动态链接两者最大的区别就在于链接的时机不一样,静态链接是在形成可执行程序前,而动态链接的进行则是在程序执行时。

一、静态链接

  • 为什么要进行静态链接

        在我们的实际开发中,不可能将所有代码放在一个源文件中,所以会出现多个源文件,而且多个源文件之间不是独立的,而会存在多种依赖关系,如一个源文件可能要调用另一个源文件中定义的函数,但是每个源文件都是独立编译的,即每个*.c文件会形成一个*.o文件,为了满足前面说的依赖关系,则需要将这些源文件产生的目标文件进行链接,从而形成一个可以执行的程序。这个链接的过程就是静态链接

  • 静态链接的原理

         由很多目标文件进行链接形成的是静态库,反之静态库也可以简单地看成是一组目标文件的集合,即很多目标文件经过压缩打包后形成的一个文件。

  • 静态链接的优缺点

        静态链接的缺点很明显,一是浪费空间,因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,如多个程序中都调用了printf()函数,则这多个程序中都含有printf.o,所以同一个目标文件都在内存存在多个副本;另一方面就是更新比较困难,因为每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。但是静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。

二、动态链接

  • 为什么会出现动态链接

        动态链接出现的原因就是为了解决静态链接中提到的两个问题,一方面是空间浪费,另外一方面是更新困难。下面介绍一下如何解决这两个问题。

  • 动态链接的原理

        动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。下面简单介绍动态链接的过程:

        假设现在有两个程序program1.o和program2.o,这两者共用同一个库lib.o,假设首先运行程序program1,系统首先加载program1.o,当系统发现program1.o中用到了lib.o,即program1.o依赖于lib.o,那么系统接着加载lib.o,如果program1.o和lib.o还依赖于其他目标文件,则依次全部加载到内存中。当program2运行时,同样的加载program2.o,然后发现program2.o依赖于lib.o,但是此时lib.o已经存在于内存中,这个时候就不再进行重新加载,而是将内存中已经存在的lib.o映射到program2的虚拟地址空间中,从而进行链接(这个链接过程和静态链接类似)形成可执行程序。

  • 动态链接的优缺点

        动态链接的优点显而易见,就是即使需要每个程序都依赖同一个库,但是该库不会像静态链接那样在内存中存在多分,副本,而是这多个程序在执行时共享同一份副本;另一个优点是,更新也比较方便,更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运行时,新版本的目标文件会被自动加载到内存并且链接起来,程序就完成了升级的目标。但是动态链接也是有缺点的,因为把链接推迟到了程序运行时,所以每次执行程序都需要进行链接,所以性能会有一定损失。

        据估算,动态链接和静态链接相比,性能损失大约在5%以下。经过实践证明,这点性能损失用来换区程序在空间上的节省和程序构建和升级时的灵活性是值得的。

  • 动态链接地址是如何重定位的呢?

        前面我们讲过静态链接时地址的重定位,那我们现在就在想动态链接的地址又是如何重定位的呢?虽然动态链接把链接过程推迟到了程序运行时,但是在形成可执行文件时(注意形成可执行文件和执行程序是两个概念),还是需要用到动态链接库。比如我们在形成可执行程序时,发现引用了一个外部的函数,此时会检查动态链接库,发现这个函数名是一个动态链接符号,此时可执行程序就不对这个符号进行重定位,而把这个过程留到装载时再进行。
 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值