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

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();
}
时间: 2024-10-07 05:45:28

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

c++ 动态判断基类指针指向的子类类型(typeid)

我们在程序中定义了一个基类,该基类有n个子类,为了方便,我们经常定义一个基类的指针数组,数组中的每一项指向都指向一个子类,那么在程序中我们如何判断这些基类指针是指向哪个子类呢? 本文提供了两种方法 (1) 自定义类id, (2)typeid 一.自定义id 如下所示基类father有两个子类son1 和 son2,我们在基类中定义类虚函数id,子类中分别重载了该函数,各个子类返回值都不同 1 class father 2 { 3 public: 4 virtual void fun() 5 {

设计模式——UML中的类图及类图之间的关系

一丶统一建模语言简介 统一建模语言(Unified Modeling Language,UML)是用来设计软件蓝图的可视化建模语言,1997 年被国际对象管理组织(OMG)采纳为面向对象的建模语言的国际标准.它的特点是简单.统一.图形化.能表达软件设计中的动态与静态信息. 统一建模语言能为软件开发的所有阶段提供模型化和可视化支持.而且融入了软件工程领域的新思想.新方法和新技术,使软件设计人员沟通更简明,进一步缩短了设计时间,减少开发成本.它的应用领域很宽,不仅适合于一般系统的开发,而且适合于并行

设计模式的学习(二)-UML中的类图及类图之间的关系

统一建模语言(Unified Modeling Language,UML)是用来设计软件蓝图的可视化建模语言,为面向对象的建模语言的国际标准.他的特点是简单,统一,图形化,能表达软件设计中的动态与静态信息. 统一建模语言能为软件开发的所有阶段提供模型化和可视化支持,而且融入了软件工程领域的新思想.新方法和新技术,使软件设计人员沟通更简明,进一步缩短的设计时间,减少开发成本,它的应用领域很宽,不仅适合与一般系统的开发,而且适合于并行与分布式系统的建模. UML从目标系统的不同角度出发,定义了用例图

UML-用例、类图、类图之间的关系详解

UML UML 一用例图 二类图 一.用例图 用例图的基本概念 由参与者.用例以及用例与用例之间的关系构成的用于描述系统功能的动态视图称为用例图. 参与者的基本概念 参与者(Actor)是指存在于系统外部并直接与系统交互的人.系统或者设备等. 参与者在画图中使用简笔人物画来表示,如下所示: 用例的基本概念 用例是系统为参与者提供的功能. 对于用例的命名,我们可以给用例取一个简单的.概括性的,一般带有动作性的词语. 用例的图形化表示,如下所示: 参与者和用例的关系 如下所示: 用例与用例之间的三个

UML中类图与类图之间的关系

一.基本概念 类 ??定义:类是指具有相同属性.方法和关系的对象的抽象,它封装了数据和行为,是面向对象程序设计(OOP)的基础.在 UML 中,类使用包含类名.属性和操作且带有分隔线的矩形来表示. 名称 格式 例子 说明 类名 字符串 Student 无 属性 [可见性]属性名:类型[=默认值] -name:String 类的成员变量 操作 [可见性]名称(参数列表)[:返回类型] +display():void 任意一个类实例对象都可以使用的行为 ??注:"可见性"表示该属性对类外的

C++ typeid动态判断基类指针指向的子类类型

typeid是c++的关键字,typeid操作符的返回结果是名为type_info的标准库类型的对象的引用(在头文件typeinfo中定义) ISO C++标准并没有确切定义type_info,它的确切定义编译器相关的,但是标准却规定了其实现必需提供如下四种操作: type_info类提供了public虚 析构函数,以使用户能够用其作为基类.它的默认构造函数和拷贝构造函数及赋值操作符都定义为private,所以不能定义或复制type_info类型的对象. 程序中创建type_info对象的唯一方

UML类图之间的关系

1. 泛化(Generalization) [泛化关系]:是一种继承关系,表示一般与特殊的关系,它指定了子类如何特化父类的所有特征和行为.例如:老虎是动物的一种,即有老虎的特性也有动物的共性. [箭头指向]:带三角箭头的实线,箭头指向父类         2. 实现(Realization) [实现关系]:是一种类与接口的关系,表示类是接口所有特征和行为的实现. [箭头指向]:带三角箭头的虚线,箭头指向接口         3. 关联(Association) [关联关系]:是一种拥有的关系,它

谈谈View类、ViewGroup类他们之间的关系

在android开发中android的UI界面都是由View和ViewGroup及其派生类组合而成的.其中,View是所有UI组件的基类,而ViewGroup是容纳这些组件的容器,其本身也是从View派生出来的. 作为容器的ViewGroup可以包含作为叶子节点的View,也可以包含作为更低层次的子ViewGroup,而子ViewGroup又可以包含下一层的叶子节点的View和ViewGroup.事实上,这种灵活的View层次结构可以形成非常复杂的UI布局,开发者可据此设计.开发非常精致的UI界

C++ 虚函数、静态联编和动态联编、抽象类

 //C++ 虚函数.静态联编和动态联编.抽象类 #include<iostream> #include<string> using namespace std; class People { private: string name; int age; public: People(string name_, int age_):name(name_), age(age_){} People(){cout << "基类默认构造函数" <&