学习笔记:c++多态、智能指针、动态内存

本文详细介绍了C++中的多态性,包括静态多态和动态多态,以及静态联编和动态联编的概念。讨论了父类指针和虚函数的工作原理,强调了虚析构函数的重要性。此外,还探讨了智能指针(unique_ptr、shared_ptr和weak_ptr)在动态内存管理中的应用,以避免内存问题。最后,概述了动态内存的分配和释放,如new、delete、malloc和free的区别及使用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、多态

1. 什么是多态

多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。

C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。为了更为详细的说明多态,此处我们划分为**静态多态 **和 **动态多态 ** 两种状态来讲解

  • 静态多态

静态多态是编译器在编译期间完成的,编译器会根据实参类型来选择调用合适的函数,如果有合适的函数可以调用就调,没有的话就会发出警告或者报错 。 该种方式的出现有两处地方: **函数重载 ** 和 泛型编程| 函数模板

int Add(int a, int b){
    return a + b;
};

double Add(double a, double b)
{
    return a + b;
};

int main()
{
    Add(10, 20);
    
    Add(10.0,20.0);  //正常代码
    
    return 0;
}
  • 动态多态

它是在程序运行时根据父类的引用(指针)指向的对象来确定自己具体该调用哪一个类的虚函数。

A *a = new A();

Father * f = new Fahter();

Father * f2 = new Child();

动态多态的必须满足两个条件:

  1. 父类中必须包含虚函数,并且子类中一定要对父类中的虚函数进行重写
  2. 通过父类对象的指针或者引用调用虚函数。

2. 父类指针

通常情况下,如果要把一个引用或者指针绑定到一个对象身上,那么要求引用或者指针必须和对象的类型一致。 不过在继承关系下,父类的引用或指针可以绑定到子类的对象,这种现象具有欺骗性,因为在使用这个引用或者指针的时候,并不清楚它所绑定的具体类型,无法明确是父类的对象还是子类的对象。

int *p = new int(3);

继承关系:

father *p = new father();

child *c = new child();

//继承的时候,有一种特殊情况:

father *p2 = new child():

1. 静态类型和动态类型

只有在继承关系下,才需要考虑静态和动态类型,这里仅仅是强调类型而已。所谓的静态类型指的是,在编译时就已经知道它的变量声明时对应的类型是什么。而动态类型则是运行的时候,数据的类型才得以确定。

只有在引用或者指针场景下,才需要考虑 静态或者动态类型。因为非引用或者非指针状态下,实际上发生了一次拷贝动作。

father *f2 = new son();
//静态类型:不需要运行,编译状态下,即可知晓 a,b的类型。
int a = 3;
string b = "abc";

//动态类型:
 //f在编译时,类型是Father ,但在运行时,真正的类型由getObj来决定。目前不能明确getObj返回的是Father的对象还是Child的对象。
Child getObj(){
    Child c ;
    return c;
};

Father &f = getObj(); 
  • 父类指针(引用)指向子类对象

父类的引用或指针可以绑定到子类的对象 , 那么在访问同名函数时,常常出现意想不到的效果。

class father{
public:
    void show(){
        cout << "father show" << endl;
    }
};
class children : public father{
public:
    void show(){
        cout << "children  show" << endl;
    }
};
int main(){
    father f = children();
    f.show(); // 打印father show
}
2. 静态联编和动态联编

程序调用函数时,到底执行哪一个代码块,由编译器来负责回答这个问题。将源码中的函数调用解释为执行特定的函数代码,称之为函数名联编。 在C语言里面,每个函数名都对应一个不同的函数。但是由于C++里面存在重载的缘故,编译器必须查看函数参数以及函数名才能确定使用哪个函数,编译器可以在编译阶段完成这种联编,在编译阶段即可完成联编也被称为:静态联编 | 早期联编。 程序在运行期间才决定执行哪个函数,这种称之为动态联编 | 晚期联编

class WashMachine{
public:
    void wash(){
        cout << "洗衣机在洗衣服" << endl;
    }
};


class SmartWashMachine : public WashMachine{
public:
    void wash(){
        cout << "智能洗衣机在洗衣服" << endl;

    }
};


int main(){
    WashMachine *w1= new WashMachine(); //父类指针指向父类对象  打印:洗衣机在洗衣服
    w1->wash();
     
    SmartWashMachine *s  = new SmartWashMachine();  //子类指针指向子类对象 打印: 智能洗衣机...
    s->wash();
    
    WashMachine *w2 = new SmartWashMachine(); //父类指针指向子类对象 打印:洗衣机在洗衣服
    w2->wash();
    
    return 0 ;
}
3. 为什么要区分两种联编

动态联编在处理子类重新定义父类函数的场景下,确实比静态联编好,静态联编只会无脑的执行父类函数。但是不能因此就否定静态联编的作用。动态联编状态下,为了能够让指针顺利访问到子类函数,需要对指针进行跟踪、这需要额外的开销。但是并不是所有的函数都处于继承状态下,那么此时静态联编更优秀些。

编写c++代码时,不能保证全部是继承体系的父类和子类,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值