静态联编,动态联编,类指针之间的关系,虚函数与多态性,纯虚函数,虚析构函数

来源:互联网 发布:oper 在js 中什么意思 编辑:程序博客网 时间:2024/05/16 23:57

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=1pfu = &zi1;pfu->name();//父类加了virtual之后,此处打印出:子类x=2,y=3pfu = &sun1;pfu->name();//父类加了virtual之后,此处打印出:孙类x=4,y=5std::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();}






































0 0
原创粉丝点击