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

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();
}






































  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涂作权的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值