idea 查看一个类的子类_C++ 类的使用(三)

 运算符的重载及类的多态

1cfe761f85e9b0c17ee4a0b1207f4a12.png

01

运算符的重载

    C++中对于内置的运算符可以自定义重载,使用关键字operator实现。

    运算符的重载方式:

    1.通过类内重载

    2.通过全局函数重载

class MyPoint{public:  MyPoint(double x, double y) :X(x), Y(y)  {  }  double X;  double Y;  //+运算符重载  MyPoint operator+(const MyPoint &pt)  {    MyPoint temp(this->X + pt.X, this->Y + pt.Y);    return temp;  }  //++MyPoint重载  MyPoint operator++()  {    MyPoint temp = *this;    this->X++;    this->Y++;    return temp;      }  //MyPoint++重载  MyPoint & operator++(int)  {    this->X++;    this->Y++;    return *this;  }  //*运算符重载  MyPoint & operator*(double d)  {    this->X *= d;    this->Y *= d;    return *this;  }  //=运算符重载  MyPoint & operator=(double d)  {    this->X = d;    this->Y = d;    return *this;  }  //=运算符重载  MyPoint & operator=(const MyPoint &pt)  {    this->X = pt.X;    this->Y = pt.Y;    return *this;  }  //仿函数  void operator()(string str)  {    cout << " 仿函数调用:" << str << endl;  }};//全局函数的方式重载运算符<<ostream & operator<out, {  out << " X = " << pt.X << "\t" << " Y = " << pt.Y << endl;  return out;}

添加测试代码:

int main(){  MyPoint p1(30, 20);  MyPoint p2(20, 30);  cout << p1 + p2;  cout << " ++p1     :" << ++p1 ;  cout << " p1++     :" << p1++ ;  cout << " p1*10    :" << p1 * 10;  p1 = 25;  cout << " p1 = 25  :" << p1;  p1 = p2;  cout << " p1 = p2  :" << p1;  p1("TestPrint");  system("pause");}

输出结果:

b8d1b08ab719bb807cd87db757df5d36.png

02

类的继承

   C++类的继承语法 

class ClassA:[继承方式] ClassB

继承方式:public/private/protect(基类private权限的对象子类无法访问)

    public:所有继承对象的权限不变

    private:所有继承对象的权限改为私有(继续派生时其子类不可访问)

    protect:所有继承对象的权限改为protect(类外无法访问,派生的子类可以访问)

一个类可以继承多个基类

class ClassA:[继承方式] ClassB,[继承方式] ClassC...

类继承时的构造及析构函数执行顺序

1.基类构造函数->子类构造函数

2.子类析构函数->基类析构函数

类作为属性时的加载及释放顺序

1.成员对象构造函数->构造函数

2.析构函数->成员对象析构函数

类继承时对于同名成员的访问:添加作用域访问

[子类实例名].[基类型]::[基类型成员]MyClass2 cls;cls.MyClass1::K = 20;cls.MyClass1::Function();


类继承时的对象模型

在类继承时,对子类分配内存空间时也会对基类的private对成员对象分配内存空间,只不过该空间被编译器隐藏,程序无法访问。

    菱形继承时的处理 如:

ClassB:public ClassAClassC:public ClassAClassD:public ClassB,public ClassC

    这种菱形的继承关系,如果ClassA、ClassB、ClassC均存在属性K,则ClassD在继承属性K的时候会存在多余的继承。针对此种情况,需要用关键字virtual把继承关系改为虚继承:

ClassB:virtual public ClassAClassC:virtual public ClassAClassD:public ClassB,public ClassC

03

类的多态

   C++类的多态分为静态多态和动态多态。

    静态多态:在程序编译阶段就已经确定函数地址。如函数重载、运算符重载。 

    动态多态:在程序运行期间才能确定函数地址。如派生类和虚函数

class BaseClass{public:  int K;  virtual void TestFunction(){  }};class ExtendClass :virtual public BaseClass{public:  int K;};

多态的对象模型:

    查看内存模型的指令:cl /d1 reportSingleClassLayout[类名] [类文件名] 需要用vs开发者命令在类文件夹下执行。

上面例子中ExtendClass的内存模型:

626e5b400ba3a53ff72bce6fa0590737.png

    子类继承父类属性时,如果使用了关键字virtual,则从父类继承下来的v(virtual)b(base)ptr(pointer)是一个虚拟基类指针,指向vbtable(虚基类表),表中的内容表达的是继承属性的偏移量,所以从父类继承下来的属性,在内存中只存在一份,不会存在多余的数据。

虚函数在继承时,从父类继承下来的v(virtual)f(function)ptr(pointer)是一个虚函数指针,指向vftable(虚函数表),表中内容表达对应函数的地址(偏移量),当子类重写了虚函数之后,该表中的函数就会被覆盖。

如果在子类中重写了父类的虚函数void TestFunction()

class ExtendClass :virtual public BaseClass{public:  int K;  void TestFunction(){  }};

此时再查看模型:

7ad9844296dd6e8c998bc2578a076aa6.png

发现vftable中的函数地址变成了自身的函数中的地址,这样就达到了函数的重写效果。

虚析构函数,由于多态在使用时采用父类的类型来操作子类数据,所以在释放时会出现子类的析构函数无法调用情况,也就是如果子类在堆区上创建了数据,则会无法释放。对于这种情况可以在父类声明虚析构函数来解决。

  virtual ~BaseClass()  {  }

    纯虚函数抽象类

纯虚函数在定义后该类将会成为抽象类不允许实例对象

纯虚函数的语法:

virtual [返回值类类型] [函数名](形参列表) = 0;

纯虚函数定义后子类必须实现,否则子类也会成为抽象类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值