1.静态联编,是程序的匹配,连接在编译阶段实现,也称为早期匹配。重载函数使用静态联编。
2.动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编。switch语句和if语句是动态联编的例子。
#include<iostream> void go(int num) { } void go(char *str) { } //class //::在一个类中 class A { public: void go(int num) { } void go(char *str) { } }; void main() { //auto p = go;在编译的阶段就能判断好的,静态联编 void(*p1)(char *str) = go;//指针函数 void(*p2)(int num) = go; //在编译的时候就确定了的叫静态联编 std::cin.get(); } //动态联编 void main1() { int num; std::cin >> num; if (num > 10) { system("calc"); } else { system("notepad"); } }
3.静态联编
普通成员函数重载可表达为两种形式
在一个类说明中重载
基类的成员函数在派生类重载。有3中编译区分方法:
(1):根据参数的特征加以区分
(2):使用”::”加以区分
例如:A::Show();
有别于B::show()
(3):根据类对象加以区分
例如:Aobj.show(); 调用 A::Show();
Bobj.show(); 调用 B::Show();
案例:
#include <iostream> //父类与之类之间的重载,同名函数会覆盖 //即使参数类型不一样,不能直接调用,必须调用父类默认生成的对象来调用 class A { public: void go() { std::cout << "A---go"; } void go(int num) { std::cout << "A---go" << num; } void go(char *str) { std::cout << "A---go" << str << "str"; } void goA(char *str) { std::cout << "A---go" << str << "str"; } }; class B :public A { public: //const 函数重载一般适用于常量对象 //非const一般适用于变量对象 void go() { std::cout << "B---go"; } void go() const { std::cout << "B---go const"; } }; void main2() { B *pb = new B; //pb->go(NULL); pb->goA("1"); //当父类和子类中出现了重名的函数的时候,即使参数类型不一样, //不能直接调用,必须调用父类默认生成的对象来调用。 //pb->go("1"); //C语言中的NULL可以打印,但是C++中的空指针式不可以打印的 pb->A::go(NULL); //当调用 pb->A::go("123"); //pb->A::go(nullptr);//C++空指针不能打印 std::cin.get(); //运行结果是:A---go1strA---go0A---go123str } void main() { B *p = new B; p->go(); const B *pb = new B; pb->go(); std::cin.get(); }
4.类指针的关系
基类指针和派生类指针与基类对象和派生类对象4种可能匹配。
A:直接用基类指针引用基类对象。
B:直接用派生类指针引用派生类对象。
C:用基类指针引用一个派生类对象。
D:用派生类指针引用一个基类对象。
5.类与指针之间的关系
Adynamic适用于虚函数
B类而言,数据是私有,代码是公有的
C指针为空,指向一个类,可以直接调用方法
D涉及内部成员会崩溃,不涉及可以执行
E父类指针引用父类对象,完全正常引用
F子类指针引用子类对象,覆盖父类的同名函数
G父类指针引用子类对象,只能引用父类中的函数
H子类指针,引用父类对象,子类不涉及内部数据的函数会调用成功
I涉及到内部数据的会调用成功,执行失败
J子类指针可以引用父类的不重名的函数
K子类指针(不是pzi->fu::print();方法)无法引用父类的同名方法
案例如下:
fu.h
#pragma once #include <iostream> class fu { public: fu(); ~fu(); char * strfu; void print(); void fufu(); }; zi.h #pragma once #include "fu.h" class zi : public fu { public: zi(); ~zi(); char *strzi; char ch[900000000]; void print(); void zizi(); }; fu.cpp #include "fu.h" fu::fu() { this->strfu = "父亲"; std::cout << "fu create" << std::endl; } fu::~fu() { std::cout << "fu delete" << std::endl; } void fu::print() { std::cout << this->strfu << "\n"; } void fu::fufu() { std::cout << "我是你爹" << "\n"; } zi.cpp #include "fu.h" fu::fu() { this->strfu = "父亲"; std::cout << "fu create" << std::endl; } fu::~fu() { std::cout << "fu delete" << std::endl; } void fu::print() { std::cout << this->strfu << "\n"; } void fu::fufu() { std::cout << "我是你爹" << "\n"; } main.cpp #include <iostream> #include"fu.h" #include "zi.h" //dynamic适用于虚函数 //类而言,数据是私有,代码是公有的 //指针为空,指向一个类,可以直接调用方法 //涉及内部成员会崩溃,不涉及可以执行 //父类指针引用父类对象,完全正常引用 //子类指针引用子类对象,覆盖父类的同名函数 //父类指针引用子类对象,只能引用父类中的函数 //子类指针,引用父类对象,子类不涉及内部数据的函数会调用成功 //涉及到内部数据的会调用成功,执行失败 //子类指针可以引用父类的不重名的函数 //子类指针(不是pzi->fu::print();方法)无法引用父类 //的同名方法 void main1() { { //fu *pfu = new fu; //delete pfu; } { //前后一致的不会出现问题 zi *pzi = new zi; delete pzi; } { //fu *pfu = new zi; //内存泄漏,理解:后面的空间大,前面的空间小,释放前面,出现内存泄露 //delete pfu; } { //fu *pfu = new fu; zi *pzi = static_cast<zi *>(new fu); delete pzi;//内存越界,超过界限释放内存,有时出错,有时无错 } std::cin.get(); } void main2() { zi *pzi(nullptr); //会调用自己的函数 pzi->zizi();//结果是:我是你儿子 std::cin.get(); } void main() { fu *pfu = new fu; zi *pzi = static_cast<zi *>(pfu); pzi->fufu();//我是你爹 pzi->zizi();//我是你儿子 pzi->fu::print();//父亲 //pzi->print(); //std::cout << pzi->strzi << std::endl; //pzi->print(); std::cin.get(); }
6.冠以关键字virtual的成员函数称为虚函数。
实现运行时多态的关键首先是要说明虚函数,另外,必须用基类指针调用派生类的不同实现版本。
7. 对于虚函数的总结:
A对于继承的情况,如果自来实现了父类的同名函数,
B当指针调用的时候会一直调用父类的函数,当成员函数加了virtual
C关键字修饰之后,子类才会调用自己的函数。
案例:
#include<iostream> //总结:对于继承的情况,如果实现了父类的同名函数 //当指针调用的时候会一直调用父类的函数,当成员函数加了virtual关键字后,子类才会调用自己的函数。 class fu { public: virtual void name() //虚函数有4个字节,32位的,实际上是一个函数指针,自动调用子类的函数覆盖虚函数 { std::cout << "父类"; std::cout << "x=" << x << "\n"; } int x; fu(int a) :x(a) { } }; class zi :public fu { public: void name() { std::cout << "子类"; std::cout << "x=" << x << ",y=" << y << "\n"; } int y; zi(int a, int b) :fu(a), y(b) { } }; class sun :public zi { public: void name() { std::cout << "孙类"; std::cout << "x=" << x << ",y=" << y << ",z=" << z << "\n"; } int z; sun(int a, int b, int c) :zi(a, b), z(c) { } }; int main(int argc, char *argv[]) { //下面的结果为8,说明虚函数的大小为4字节 std::cout << sizeof(fu) << std::endl; fu fu1(1); zi zi1(2, 3); sun sun1(4, 5, 6); fu *pfu; pfu = &fu1; pfu->name();//打印出:父类x=1 pfu = &zi1; pfu->name();//父类加了virtual之后,此处打印出:子类x=2,y=3 pfu = &sun1; pfu->name();//父类加了virtual之后,此处打印出:孙类x=4,y=5 std::cin.get(); }
8:纯虚函数
#include <iostream> using namespace std; //因为base其中有纯虚函数,所以不能实例化了。 class base { public: int num; void goA() { } //下面是一个纯虚函数 virtual void show()=0; //带了纯虚函数就是抽象类 //可以有实现,也可以没有实现,可以放在内部外部 }; class run:public base { public: void show() { std::cout << "run show();" << std::endl; } }; int main() { cout << "Hello World!" << endl; //base base1;抽象类无法实例化 run runInstance; runInstance.show(); return 0; } 运行结果:
案例2:
#include<iostream> class base { public: //纯虚函数,有无定义都可以 //有一个纯虚函数,都是抽象类,无法实例化 virtual void run() = 0 //限定一个类不能实例化,作为专门的接口,QT里面不可以在内部定义。 { std::cout << "base run\n"; } virtual ~base() { } }; //抽象类不可以用于函数的参数以及返回值类型 //抽象类指针是可以 base * test(base *p) { base *pbase(nullptr); return pbase; } class boy :public base { public: void run() { std::cout << "男孩奔跑\n"; } }; class girl :public base { public: void run() { std::cout << "女孩奔跑\n"; } }; void main() { //抽象类无法实例化对象,可以实话指针 //纯虚函数与抽象类与虚函数起到接口的作用 //用同一个接口完成不同的功能 //纯虚函数完全就是为了接口的存在,有了纯虚函数的类无法实例化 //虚函数占4个字节,就是指针,函数指针 boy boy1; girl girl1; base *p(nullptr); p = &boy1; p->run(); p = &girl1; p->run(); std::cin.get(); }
9.虚析构函数,在析构函数的前面加上virtual
案例代码如下:
#include <iostream> using namespace std; class A { public: //构造函数不可以是虚函数,如果是了就不可以再构造, //没有办法创建子类中的父类对象 A() { std::cout << "a create" << std::endl; } //虚析构函数,让父类指针正确的释放子类对象的内存 virtual ~A() { std::cout << "a delete" << std::endl; } }; class B:public A { public: B()//B创建自动调用A的构造函数 { std::cout << "b create" << std::endl; } ~B()//B析构的时候会自动调用A的析构函数 { std::cout << "b delete" << std::endl; } }; int main() { A *p = new B; delete p; return 0; }
如果析构函数前没有加virtual,运行结果如下:
如果析构函数前加了virtual,运行结果如下:
10. 者多个虚函数,或者多个纯虚函数都占四个字节
案例如下:
#include <iostream> using namespace std; //抽象类也可以实现继承 //仅有一个指针指向虚函数表 //1个或者多个虚函数,或者多个纯虚函数都占四个字节 //一个指针存储了虚函数标的地址 class basebase { public: virtual void show() = 0; virtual void hide() = 0; virtual void run() = 0; }; class base { public: virtual void show(){} virtual void hide(){} virtual void run(){} }; int main() { cout << sizeof(basebase) << endl;//结果是4 cout << sizeof(base) << endl;//结果为4 return 0; }
11.子类只有都实现了基类中的纯虚函数之后才可以实例化
案例说明如下:
#include "mainwindow.h" #include <QApplication> #include<QPushButton> #include<QLabel> #include<iostream> //抽象类也可以实现继承 class basebase { public: virtual void show()=0; virtual void hide()=0; }; //接口,操作不同的类对象的方法 class base :public basebase { public: virtual void resize(int x,int y)=0; virtual void move (int cx,int cy)=0; }; class myself: public base { public: //只要有一个接口的没有实现,抽象类 //把所有就接口都实现了的类才可以实例化 void show(){} void hide(){} }; class mydialog: public base { public: MainWindow w; void show() { w.show(); } void hide() { w.hide(); } void resize(int x,int y) { w.resize(x,y); } void move (int cx,int cy) { w.move(cx,cy); } }; class mybutton: public base { public: QPushButton w; void show() { w.show(); } void hide() { w.hide(); } void resize(int x,int y) { w.resize(x,y); } void move (int cx,int cy) { w.move(cx,cy); } }; class mylabel: public base { public: QLabel w; void show() { w.show(); } void hide() { w.hide(); } void resize(int x,int y) { w.resize(x,y); } void move (int cx,int cy) { w.move(cx,cy); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); //myself my1; return a.exec(); } int main1(int argc, char *argv[]) { QApplication a(argc, argv); mydialog dialog1; mybutton button1; mylabel label1; //只有在.pro中加上CONFIG += c++11后nullptr才可以使用,因为其是C++11特性 base *pbase(nullptr); pbase =&dialog1; pbase->show(); pbase->resize(100,200); pbase =&button1; pbase->show(); pbase->resize(200,200); pbase=&label1; pbase->show(); pbase->resize(300,200); return a.exec(); }