多态

多态就是说同一个名字的函数可以有多种不同的功能。分为编译时的多态运行时的多态。编译时的多态就是函数重载,包括运算符重载,编译时根据实参确定调用哪个函数。运行时的多态则和虚函数、继承有关。

多态实现的三步:1)有继承, 2)虚函数重写, 3)基类指针或引用指向派生类对象。

int main() {
    Base* ptr;  // 基类指针
    Derived obj;  // 派生类对象
    ptr = &obj;  // 基类指针指向派生类对象
​
    ptr->show();  // 通过基类指针调用虚函数,输出"Derived class"
    return 0;
}

重载是在编译时确定的,不同的参数会生成不同的函数名,pbd文件里面可以看到。

虚函数是运行时确定的,new对象的时候才会生成虚表指针,里面放的是函数指针,父类构造函数调用虚函数,子类重写了这个虚函数,new子类的时候不会调到重写的函数,只会把它当成普通函数调用。

构造函数不能是虚函数。

静态函数不能是虚函数,没有this指针。

析构函数不是虚函数一定会发生内存泄漏吗? 一定。

指针和引用

指针传递的本质是值传递复制实参的地址到函数的栈中,然后在形参中对地址取值操作。而引用的形参是给实参起了一个别名,可以直接操控形参从而实现对实参的控制。

1.指针可以为空,而引用不可以指向空值。 2.指针可以不初始化,引用必须初始化。这意味着引用不需要检测合法性 3.指针可以随时更改指向的目标,而引用初始化后就不可以再指向任何其他对象

右值引用

左值右值可以简单用能不能取地址来区分,一般来说能取地址就是左值,不能取地址就是右值,比如int a =1;a就是左值,1就是右值。int &b=a;就是左值引用,直接叫引用即可。

const左值引用不会修改指向值,因此可以指向右值,这也是为什么要使用const &作为函数参数的原因之一,如std::vectorpush_back

void push_back (const value_type& val);

右值引用的标志是&&,例如int &&a = 1;

智能指针

shared_ptr,unique_ptr,weak_ptr。第一个实现原理是同一个内存空间每多一个指针指向就计数加1,如果计数变为0就释放内存空间。第二个是计数只能为1,第三个只能指向该内存空间而没有所有权。主要用于辅助第一个指针,防止出现互锁。Shared_ptr当用普通指针初始化的时候,只能使用一次普通指针。它还可以自定义释放函数。Unique_ptr没有拷贝构造函数。借助 weak_ptr 类型指针, 我们可以获取 shared_ptr 指针的一些状态信息,比如有多少指向相同的 shared_ptr 指针、shared_ptr 指针指向的堆内存是否已经被释放等等。在构建 weak_ptr 指针对象时,可经常利用已有的 shared_ptr 指针为其初始化。为什么使用make_shared,因为指针和计数总是一起访问,make_shared可以保证这两者内存连续,提高cache命中率。

虚表

虚表是在编译期创建的,而不是在运行时。编译器在编译含有虚函数的类时,会为该类生成一个虚表。虚表包含了该类的虚函数指针,指向这些虚函数的具体实现。

虽然虚表本身在编译期生成,但虚表指针(vptr)是在运行时设置的。具体来说,当一个对象被创建时,构造函数负责设置该对象的虚表指针,使其指向正确的虚表,构造函数不能为虚函数。