C++ 重载运算符/函数
https://www.runoob.com/cplusplus/cpp-overloading.html
C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。
重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。
重载函数
在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。
重载运算符
可以重定义或重载大部分 C++ 内置的运算符。这样就能使用自定义类型的运算符。
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
Box operator+(const Box&);
C++ 多态 & virtual关键字
C++ 多态
当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
#include <iostream>
using namespace std;
class Shape
{
protected:
int width, height;
public:
Shape (int w = 0, int h = 0)
{
width = w;
height = h;
}
virtual ~Shape(){}
virtual int area() // virtual 虚函数 用于多态
{
cout << "Parent calss area" << endl;
return 0;
}
};
class Rectangle : public Shape
{
public:
Rectangle (int w = 0, int h = 0) : Shape(w, h) { }
int area()
{
cout << "Rectangle area " << (width * height) << endl;
return (width * height);
}
};
class Tritangle : public Shape
{
public:
Tritangle (int w = 0, int h = 0) //: Shape(w, h) { }
{
width = w;
height = h;
}
int area()
{
cout << "Trictangle area " << (width * height / 2) << endl;
return (width * height / 2);
}
};
int main()
{
Shape* shape;
Rectangle rect(2,4);
Tritangle trit(2,4);
rect.area();
trit.area();
shape = ▭
shape->area();
shape = &trit;
shape->area();
return 0;
}
当基类 Shape 中的 area() 函数不是虚函数时,调用函数 area() 被编译器设置为基类中的版本,shape->area();
输出结果为
“Parent calss area”
这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为 area() 函数在程序编译期间就已经设置好了。
在 Shape 基类中,area() 的声明前放置关键字 virtual,将在派生类中被重写的成员函数设置为虚函数,其含义是:当通过基类的指针或者引用调用该成员函数时,将根据指针指向的对象类型确定调用的函数,而非指针的类型,这种操作被称为动态链接,或后期绑定。
虚函数
1.只需将基类中的成员函数声明为虚函数即可,派生类中重写的virtual函数自动成为虚函数,并告诉编译器不要静态链接到该函数;
2.基类中的析构函数必须为虚函数,否则会出现对象释放错误。以上例说明,如果不将基类的析构函数声明为virtual,那么在调用delete trit;语句时将调用基类的析构函数,而不是应当调用的派生类的析构函数,从而出现对象释放错误的问题。
3.虚函数的使用将导致类对象占用更大的内存空间。对这一点的解释涉及到虚函数调用的原理:编译器给每一个包括虚函数的对象添加了一个隐藏成员:指向虚函数表的指针。虚函数表(virtual function table)包含了虚函数的地址,由所有虚函数对象共享。当派生类重新定义虚函数时,则将该函数的地址添加到虚函数表中。无论一个类对象中定义了多少个虚函数,虚函数指针只有一个。相应地,每个对象在内存中的大小要比没有虚函数时大4个字节(32位主机,不包括虚析构函数)。
4.重写函数的特征标(包括参数的数目、类型和顺序)以及返回值必须与基类函数一致,否则将覆盖基类函数;
5.重写不同于重载。(同一个类,内部的同名函数具有不同的参数列表称为重载;重写则是派生类对基类同名函数的“本地改造”,要求函数特征标完全相同。当然,返回值类型不一定相同(可能会出现返回类型协变的特殊情况)。)
纯虚函数
一个类的成员函数被声明为纯虚函数,以便在派生类中重新定义该函数更好地适用于对象,则意味着该类是ABC(Abstract Base Class,抽象基类),即只能被继承,而不能用来声明对象。纯虚函数通常需要在类声明的后面加上关键词“=0”。 如 virtual int area() = 0;
当然,声明为纯虚函数并不意味着在实现文件中不可对其进行定义,只是意味着不可用抽象基类实现一个具体的对象。
虚基类
在c++中,派生类可以继承多个基类。问题在于:如果这多个基类又是继承自同一个基类时,那么派生类是不是需要多次继承这“同一个基类”中的内容?虚基类可以解决这个问题。
简而言之,虚基类可以使得从多个类(它们继承自一个类)中派生出的对象只继承一个对象。
class base
{
int b;
public:
virtual void test(){ cout<<"基类方法!"<<endl; }
virtual ~base(){};
};
class mytest:virtual public base
{
};
class mytest2:virtual public base
{
};
class mytest3:public mytest,public mytest2
{
};
cout<<sizeof(mytest)<<endl; //输出12
cout<<sizeof(mytest2)<<endl; //输出12
cout<<sizeof(mytest3)<<endl; //输出16,若在base中添加一个int型成员,则输出20
base称为mytest类的虚基类。假设base还是另外一个类mytest2的虚基类,对于多重继承mytest和mytest2的子类mytest3而言,base的部分只继承了一次。
mytest类与mytest2类在虚继承自base类后,添加了一个隐藏的成员——指向虚基类的指针,占4个字节。而base类本身占8个字节,因此它们的大小均为12。而对非虚继承而言,是不需要这样的一个指针的。而mytest3类的大小为sizeof(base)+sizeof(mytest-base)+sizeof(mytest2-base),即16。
1.若一个类多重继承自具有同一个基类的派生类时,调用同名成员函数时会出现二义性。为了解决这个问题,可以通过作用域解析运算符澄清,或者在类中进行重新定义;
2.继承关系可能是非常繁复的。一个类可能多重继承自别的类,而它的父类也可能继承自别的类。当该类从不同的途径继承了两个或者更多的同名函数时,如果没有对类名限定为virtual,将导致二义性。当然,如果使用了虚基类,则不一定会导致二义性。编译器将选择继承路径上“最短”的父类成员函数加以调用。该规则与成员函数的访问控制权限并不矛盾。也就是说,不能因为具有更高调用优先级的成员函数的访问控制权限是"private",而转而去调用public型的较低优先级的同名成员函数。
参考链接:https://blog.csdn.net/xbb123456rt/article/details/81986691