【C++知识树】详述virtual的四种用法

virtual用法一:修饰父类中的普通函数

virtual用法二:修饰析构函数

virtual用法三:修饰继承性 

Virtual用法四:纯虚函数

virtual用法一:修饰父类中的普通函数

被修饰的函数称为虚函数, 是C++中多态的一种实现(多说一句,多态分编译时多态-通过重载实现和运行时多态-通过虚函数实现)。 也就是说用父类的指针或者引用指向其派生类的对象,当使用指针或引用调用函数的时候会根据具体的对象类型调用对应对象的函数(需要两个条件:父类的函数用virtual修饰和子类要重写父类的函数)。

使用virtual修饰的函数会根据实际对象的类型来调用,没有使用virtual修饰的根据指针的类型来调用。虚函数最关键的特点是“动态联编”,它可以在运行时判断指针指向的对象,并自动调用相应的函数。

下面用两个例子来说明:

例一: 

#include <iostream>

class father {
public:
	void func1() {std::cout << "this is father func1" << std::endl;}
	virtual void func2() {std::cout << "this is father func2" << std::endl;
}

class son:public father {
public:
	void func1() {std::cout << "this is son func1" << std::endl;}
	void func2() {std::cout << "this is son func2" << std::endl;
}

int main() {
	father *f1 = new son();
	f1.func1();
	f1.func2();
	return 0;
}

output:

this is father func1
this is son func2

 例二:

下面这段代码打印出的结果为B,但是当把A类中的virtual去掉之后打印出的就为A。当基类中没有virtual的时候,编译器在编译的时候把p看做A类的对象,调用的自然就是A类的方法。但是加上virtual之后,将dispaly方法变成了虚方法,这样调用的时候编译器会看调用的究竟是谁的实例化对象,这样就实现了多态的效果。也就是说,当基类的派生类中有重写过基类的虚方法的时候,使用基类的指针指向派生类的对象,调用这个方法实际上调用的会是派生类最后实现的方法。

#include<iostream> 
using namespace std;
class A{
public:
     virtual  void  display(){  cout<<"A"<<endl; }
     };
     
class B :  public A{
public:
            void  display(){ cout<<"B"<<endl; }
     };
     
void doDisplay(A *p)
{
    p->display();
    delete p;
}
 
int main(int argc,char* argv[])
{
    doDisplay(new B());
    return 0;
}

output: 

B

virtual用法二:修饰析构函数

修饰析构函数与上面讲到的使用方法和原理相同,虚析构函数在销毁时会调用对象的析构函数,这样就不会出现像有的数据成员没有销毁导致内存泄露的问题或者程序直接崩溃。

下面也用两个例子说明:

例一: 

class GrandFather {
public:
	GrandFather() {std::cout << "construct grandfather" << std::endl;}
	~GrandFather() {std::cout << "destruct grandfather" << std::endl;}
};

class Father:public GrandFather{
public:
	Father() {std::cout << "construct father" << std::endl;}
	~Father() {std::cout << "destruct father" << std::endl;}
};

class Son:public Father{
public:
	Son() {std::cout << "construct son" << std::endl;}
	~Son() {std::cout << "destruct son" << std::endl;}
};

int main() {
	Father *f = new Son();
	delete f;
	return 0;
}

output:

construct grandfather
construct father
construct son
destruct father
destruct grandfather

我们发现没有调用son的析构函数,当将Father或者GrandFather其中一个的析构函数修改为virtual后输出就变为了

construct grandfather
construct father
construct son
destruct son
destruct father
destruct grandfather

 例二:

#include<iostream>
using namespace std;
class Person{
 public:        Person()  {name = new char[16];cout<<"Person构造"<<endl;}
      virtual  ~Person()  {delete []name;cout<<"Person析构"<<endl;}
 private:
         char *name;
         };
         
class Teacher :virtual public Person{
public:         Teacher(){ cout<<"Teacher构造"<<endl; }
              ~Teacher(){ cout<<"Teacher析构"<<endl; }
};

class Student :virtual public Person{
public:         Student(){ cout<<"Student构造"<<endl; }
              ~Student(){ cout<<"Student析构"<<endl; }
};

class TS : public Teacher,public Student{
public:             TS(){ cout<<"TS构造"<<endl; }
                 ~TS(){ cout<<"TS析构"<<ENDL; }
};

int main(int argc,char* argv[])
{
Person *p = new TS();
delete p;
return 0;
}

 output:

Person构造
Teacher构造
Student构造
TS构造
TS析构
Student析构
Teacher析构
Person析构

但是当我们把Person类中析构前面的virtual去掉之后的运行结果为: 

Person构造
Teacher构造
Student构造
TS构造
Person析构
程序崩溃

virtual用法三:修饰继承性 

假如有这种场景,一个类继承两个或者更多的父类,但是这些父类里又有一些有共同的父类,会出现什么情况呢?

下面给出两个例子:

例一: 

#include<iostream> 
using namespace std;
class Person{
   public:    Person(){ cout<<"Person构造"<<endl; }
           ~Person(){ cout<<"Person析构"<<endl; }
};

class Teacher : virtual public Person{
   public:    Teacher(){ cout<<"Teacher构造"<<endl; }
            ~Teacher(){ out<<"Teacher析构"<<endl; }
};

class Student : virtual public Person{
  public:      Student(){ cout<<"Student构造"<<endl; }
             ~Student(){ cout<<"Student析构"<<endl; }
};

class TS : public Teacher,  public Student{
public:   TS(){ cout<<"TS构造"<<endl; }
          ~TS(){ cout<<"TS析构"<<endl; }
};
int main(int argc,char* argv[])
{
    TS ts;
    return 0;
}

output:

Person构造 
Teacher构造 
Student构造 
TS构造 
TS析构 
Student析构 
Teacher析构 
Person析构 

当Teacher类和Student类没有虚继承Person类的时候,也就是把virtual去掉时候终端输出的结果为:

我们在构造TS的时候需要先构造他的基类,也就是Teacher类和Student类。而Teacher类和Student类由都继承于Person类。这样就导致了构造TS的时候实例化了两个Person类。

Person构造
Teacher构造
Person构造
Student构造
TS构造
TS析构
Student析构
Person析构
Teacher析构
Person析构

例二:

class GrandFather {
public:
	GrandFather() {std::cout << "construct grandfather" << std::endl;}
	~GrandFather() {std::cout << "destruct grandfather" << std::endl;}
};

class Father1:public GrandFather{
public:
	Father1() {std::cout << "construct father1" << std::endl;}
	~Father1() {std::cout << "destruct father1" << std::endl;}
};

class Father2:public GrandFather{
public:
	Father2() {std::cout << "construct father2" << std::endl;}
	~Father2() {std::cout << "destruct father2" << std::endl;}
};

class Son:public Father1, Father2{
public:
	Son() {std::cout << "construct son" << std::endl;}
	~Son() {std::cout << "destruct son" << std::endl;}
};

int main() {
	Father *f = new Son();
	delete f;
	return 0;
}

output:

construct grandfather
construct father1
construct grandfather
construct father2
construct son
destruct son
destruct father2
destruct grandfather
destruct father1
destruct grandfather

通过这个例子我们看到创建一个son会创建两个grandfather,不符合我们的预期啊,而且还可能会导致程序挂掉。这里就请virtual出场了,当把father1和father2继承grandfather修改为virtual继承(也就是在public前面加一个virtual)的时候输出会变成这样:
output:

construct grandfather
construct father1
construct father2
construct son
destruct son
destruct father2
destruct father1
destruct grandfather

Virtual用法四:纯虚函数

纯虚函数的定义是在虚函数的后面加一个=0。定义了纯虚函数的类是一个抽象类。


纯虚函数需要注意这几点:
1.定义了纯虚函数的类不能够实例化,也就是不能够创建对象
2.继承了含有纯虚函数的父类的子类如果没有实现纯虚函数也不能够实例化

virtual void func() = 0;

 

 

  • 11
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
C++ 四叉是一种基于分治思想的数据结构,常用于处理二维平面上的数据。它将空间划分为四个象限,每个象限又可以继续划分成四个象限,以此类推,直到空间被划分为足够小的块为止。每个块可以存储多个数据点,或者指向更小的子块。 四叉可以用来解决许多空间相关的问题,如碰撞检测、区域查找、图像处理等。在实现时,可以使用递归算法,也可以使用迭代算法。常见的操作包括插入数据点、删除数据点、查找数据点、遍历整棵等。 以下是一个简单的 C++ 四叉实现的示例代码: ```c++ #include <iostream> using namespace std; struct Point { int x, y; Point(int _x, int _y) : x(_x), y(_y) {} }; struct QuadtreeNode { Point* point; QuadtreeNode* children[4]; QuadtreeNode() { point = nullptr; for (int i = 0; i < 4; i++) { children[i] = nullptr; } } }; class Quadtree { public: Quadtree(int _width, int _height) : root(nullptr), width(_width), height(_height) {} void insert(Point* p) { root = insert(root, p, 0, 0, width, height); } bool contains(Point* p) { return contains(root, p, 0, 0, width, height); } private: QuadtreeNode* root; int width, height; QuadtreeNode* insert(QuadtreeNode* node, Point* p, int left, int top, int right, int bottom) { if (node == nullptr) { node = new QuadtreeNode(); } if (node->point == nullptr) { node->point = p; } else { int midX = (left + right) / 2; int midY = (top + bottom) / 2; if (p->x < midX) { if (p->y < midY) { node->children[0] = insert(node->children[0], p, left, top, midX, midY); } else { node->children[1] = insert(node->children[1], p, left, midY, midX, bottom); } } else { if (p->y < midY) { node->children[2] = insert(node->children[2], p, midX, top, right, midY); } else { node->children[3] = insert(node->children[3], p, midX, midY, right, bottom); } } } return node; } bool contains(QuadtreeNode* node, Point* p, int left, int top, int right, int bottom) { if (node == nullptr) { return false; } if (node->point != nullptr) { return node->point->x == p->x && node->point->y == p->y; } else { int midX = (left + right) / 2; int midY = (top + bottom) / 2; if (p->x < midX) { if (p->y < midY) { return contains(node->children[0], p, left, top, midX, midY); } else { return contains(node->children[1], p, left, midY, midX, bottom); } } else { if (p->y < midY) { return contains(node->children[2], p, midX, top, right, midY); } else { return contains(node->children[3], p, midX, midY, right, bottom); } } } } }; int main() { Quadtree quadtree(100, 100); Point p1(10, 20); Point p2(30, 40); quadtree.insert(&p1); quadtree.insert(&p2); cout << quadtree.contains(&p1) << endl; // 输出 1 cout << quadtree.contains(&p2) << endl; // 输出 1 Point p3(50, 60); cout << quadtree.contains(&p3) << endl; // 输出 0 return 0; } ``` 该示例代码实现了一个简单的四叉,用于存储坐标在二维平面上的点。其 `Point` 结构体表示一个点,`QuadtreeNode` 结构体表示一个四叉节点,`Quadtree` 类表示一个四叉。 `insert` 方法用于插入一个点,`contains` 方法用于查找一个点是否存在。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值