零蚀
多态
-
静态多态
静态多态是编译期完成的,编译器会根据实参的类型来选择调用合适的函数,如果有就会调用。 在继承关系下,父类指针或者引用可以绑定到子类的对象上,在使用时不能确定是父类对象还是子类对象。
class Parent{ public: void parent_say(){ } }; class Child:public Parent{ public: void child_say(){ } }; int main() { Parent *parent=new Child(); // Parent parent=Child(); parent->parent_say(); //只能调用父类的方法。 return 0; }
在编译的时候,直接认定了parent是父类的类型,根本没有看右边返回的对象是什么。也就是父类的指针可以指向子类的对象。这种编译期的判定称为静态联编,而静态联编的效率高于动态联编,不需要在运行时还做过多的开销,所以默认就是静态联编。
-
动态多态
在运行时会考虑等式右边被创建的对象,所以在使用泛型的过程中,会默认指定对象为右侧的初始化类。例如虚函数的应用。
-
虚函数
虚函数使用了多态的机制,在将父类的指针指向子类的实例的前提下,通过父类指针或者引用拿到子类的成员函数,虚函数的申明,只要在函数前面添加virtual。
class Parent{ public: virtual void say(){ cout << "parent" << endl; } }; class Child:public Parent{ public: //virtual void say ,加不加virtual void say(){ cout << "child" << endl; } }; int main() { // 走父类的同名函数 Parent *parent=new Parent(); parent->say(); // 走子类的同名函数 Child *child=new Child(); child->say(); // 父类有virtual情况下,走子类的同名函数 Parent *parent=new Child(); parent->say(); return 0; }
父类和子类具有同名函数时,虚函数添加时候,虚函数会给每一个对象赋予一个隐藏的成员指针,他指向一个数组,数组里存放着对象的所有的函数地址,这个数组称为虚函数表,父类包含的指针指向父类的虚函数表,子类包含的指针指向子类的虚函数表。
Parent obj=Child(); obj.say(); //parent Parent *obj1=new Child(); obj1->say();//child
虚函数的同名函数,只能走其中一个,必须同名,使用泛型时候,父类会指向子类的方法。
构造函数不能是虚函数,在构造的时候父类和子类的构造函数必须都走。析构函数只有在都是虚函数时(只需要在父类析构添加virtual),父子类都会走析构,如果正常的泛型情况下,不会走子类的析构。⚠️如果父类函数和子类函数出现了同名函数,就应该加上虚函数。一般需要将析构函数设置为虚函数。
-
override
只是用来标明是否是父类的重写的方法。
class Parent{ public: virtual void say(){ cout << "parent" << endl; } }; class Child:public Parent{ public: void say() override { cout << "child" << endl; } };
-
final
final禁止修饰的函数被重写,禁止修饰的类被继承。
class Object final{}
-
default_delete
delete表示没有这个函数,不给调用,删除了默认的。
default表示默认实现的函数。Child()=delete; Child()=default;
-
纯虚函数
类似于java的接口,它没有函数体。
class Parent{ public: virtual void say()=0; void function(){ } };
写了纯虚函数,表示这个类是抽象类,这个=0的函数是纯虚函数。继承抽象类的子类也是一个抽象类。
class Parent{ public: virtual void say() =0; }; class Child:public Parent{ public: void say() override { cout << "fda" << endl; } };
智能指针
-
介绍
c++将指针的操作给程序员,但是在程序运行过程中经常可能由于程序的异常导致指针无法释放,或者形成野指针,或者内存释放,指针并没有设置为nullptr,所以内存会越来越多被占用,程序不得不重启解决指针问题。
我们之前用的delete是将堆内存做个标记,表示这个内存可以被回收,但是他不会修改指针的指向。而且重复释放,可能会出现问题,也可能同时进行,不发生问题。忘记释放指针,会导致内存泄漏。 -
智能指针使用
C++ 11提供的智能指针分为3中,他们会自己回收堆内存,分别为unique_ptr,shaerd_ptr,weak_ptr,智能指针和指针没有什么太大差别,唯一不同是他们会自动回收指针的内存。
-
unique_ptr
多次删除导致错误
int *a =new int(3); int *b=a; delete a; delete b;
智能指针的运用
智能指针不允许两个智能指针指向相同的区域,
//智能指针是对指针的包装(不能重复指向一个地址) unique_ptr<int> s(new int(8)); unique_ptr<Child> s1(new Child); unique_ptr<int> s2=move(s); // s2=s;不能重复指向同一个地址 // 重新指向一个指针 s2.reset(new int(5)); s1->say();
-
shared_ptr
可以对个指针对象指向同一个地址。
//智能指针是对指针的包装(不能重复指向一个地址) shared_ptr<int> s(new int(8)); shared_ptr<int> s1=s; shared_ptr<int> s2=s; shared_ptr<int> s3=s; // 打印有多少指针指对象向同一个地址 cout << s.use_count()<< endl; // 清空指针的指向 s2.reset(); // or s2= nullptr; // 获取指针对象的指针内容 cout << *s1.get() << endl;
共享指针问题
shared_ptr免不了会有死锁的情况,Parent里面有child对象,Child里有Parent对象。
class Parent{ public: Parent(){ cout << "Parent构造" << endl; } ~Parent(){ cout << "Parent析构" << endl; } void set(shared_ptr<Child> c){ child=c; } shared_ptr<Child> child; }; class Child{ public: Child(){ cout << "Child构造" << endl; } ~Child(){ cout << "Child析构" << endl; } void set(shared_ptr<Parent> p){ parent=p; } shared_ptr<Parent> parent; }; int main() { // 会调用析构 shared_ptr<Parent> p(new Parent()); shared_ptr<Child> c(new Child()); // 如果不执行以下代码,两个类的析构函数都会执行 p->set(c); c->set(p); return 0; } // print //Parent构造 //Child构造
在share_ptr进行释放操作的时候,发现类之间存在相互引用,然后进行计数,如果程序结束会将直接指向扣除,但是相互引用无法扣除,也就是形成环形引用的情况(死锁),则无法释放对应的对象。借此需要用到weak_ptr
-
weak_ptr
他指向一个share_ptr指针,但是他不会count,也就是将一个weak_ptr绑定到一个shared_ptr上,一旦最后一个shared_ptr被销毁,对象就被销毁了。weak_ptr是用来解决环形引用的问题。
class Parent{ public: Parent(){ cout << "Parent构造" << endl; } ~Parent(){ cout << "Parent析构" << endl; } void set(shared_ptr<Child> c){ child=c; } weak_ptr<Child> child; }; class Child{ public: Child(){ cout << "Child构造" << endl; } ~Child(){ cout << "Child析构" << endl; } void set(shared_ptr<Parent> p){ parent=p; } weak_ptr<Parent> parent; }; int main() { // 会调用析构 shared_ptr<Parent> p(new Parent()); shared_ptr<Child> c(new Child()); p->set(c); c->set(p); return 0; } //print //Parent构造 //Child构造 //Child析构 //Parent析构
动态内存
-
new & delete
-
new
- new的底层是对c语言的malloc的包装
- new内存申请成功之后会返回一个该内存的地址
- new内存申请失败会抛出异常
- new 申请成功,如果是程序员定义的类型,会走其构造函数
-
delete
- delete的底层是对c语言的free的包装
- delete用于释放空间,如果是空指针则不会做任何操作(但是重复删除,只是删除内存没有修改指针指向,会有问题)
- delete的对象是自定义类型,会执行析构函数
-
-
malloc和free
int *a=(int*)malloc(sizeof(int)); free(a);
-
malloc
- malloc申请成功返回的是void*类型的指针,粗腰将void*指针转换成我们需要的类型。
- malloc要求定制申请的内存大小,而new由编译器自行计算。
- 申请失败返回的是NULL(内存不足)
- 不会执行自定义类型的构造函数
-
free
- 空指针释放没有问题,非空指针多次释放会有问题
- 不执行析构
-
-
动态数组
// 创建动态数组 int *score=nullptr; int size; score=new int[size]; //回收数组空间 delete []score; score=nullptr;
动态数组的item构造
// 会默认走10次无参构造方法,因为new默认打开构造方法的,如果想走其他构造参数会很麻烦。 Obj *obj=new Obj[10];
allocator
// 创建动态数组 allocator<Parent> temp; // 申请内存 auto area = temp.allocate(10); /** * construct (内存地址,...构造函数的形参) */ temp.construct(area,"context"); temp.construct(area+1,"context1"); temp.construct(area+2,"context2"); // 取值 cout << (area+1)->gender << endl; // 执行析构函数 temp.destroy(area+1); // 资源回收,释放空间 // deallocate(空间index,无实际意义) temp.deallocate(area+2,5);
🔗 前言
🔗 C++ 高级列表
🔗 NO.1 C++ 基础
🔗 NO.2 C++ 指针
🔗 NO.3 C++ 特殊函数
🔗 NO.4 C++ 重载&继承&lambda
🔗 NO.6 C++ I/O
🔗 NO.7 C++ 模版编程&容器
🔗 NO.8 C++ 常用函数&线程
🔗 NO.9 C++ QT 入门
🔗 NO.10 C++ QT 绘制&自定义组合控件
🔗 NO.11 C++ QT 绘制