c++ OOP(2)

发布时间 2023-10-09 15:55:16作者: dctwan

运算符重载

重新定义+-*/操作,对同类对象使用,以时间类Time为例子进行理解

Time.h

class Time{
    private:
        int hour, minute;
    public:
        Time();
        Time(int h, int m);
        void show_time();
        Time operator+ (const Time& t);
};

Time.cpp

#include "Time.h"
#include <iostream>
using namespace std;

Time::Time(){
    hour = minute = 0;
}

Time::Time(int h, int m){
    hour = h;
    minute = m;
}

void Time::show_time(){
    cout << hour << "h:" << minute << "min\n";
}

Time Time::operator+(const Time& t){    //使用引用`const Time&`,相比传值,效果相同,效率更高,内存占用更小
    Time sum;
    sum.minute = (this->minute + t.minute) % 60;	//这是成员函数,可以直接访问private成员变量
    sum.hour = this->hour + t.hour + (this->minute + t.minute) / 60;
    return sum;
}

test.cpp

#include <iostream>
#include "Time.h"

int main(){
    Time a = Time(2, 35);
    Time b = Time(2, 40);
    Time c = a + b;		//+号左侧是调用重载运算符的对象,右侧是传参对象
    # Time d = a + b + c;	//也是正确的
    c.show_time();	//output: 5h:15min
    return 0;
}

编译运行

g++ Time.cpp test.cpp

继承

基类,派生类

通常将基类和派生类的声明定义在同一个.h文件中,将基类和派生类的实现定义在同一个.cpp文件中

PS.h

#include <string>
using namespace std;

class Person{
    private:
        string name;
        int age;

    public:
        Person();
        Person(string n, int a);
        void person_info();
};

class Student : public Person{   // `:` 继承语法 派生类:基类
    private:    //派生类继承基类的成员变量,并添加自己的成员变量
        int score;

    public:     //派生类继承基类的成员函数,并添加自己的成员函数
        Student();
        Student(string n, int a, int s);    //构造函数必须给新成员和继承的成员提供数据
        void stu_info();
};

PS.cpp

#include "PS.h"
#include <iostream>
#include <string>
using namespace std;

Person::Person(){}

Person::Person(string n, int a){
    name = n;
    age = a;
}

void Person::person_info(){
    cout << name << "\t" << age << "\t";
}

Student::Student(){}

//注意派生类构造函数的实现  
//通过成员初始化列表将基类信息传递给基类构造函数
Student::Student(string n, int a, int s) : Person(n, a){
    score = s;
}

void Student::stu_info(){
    cout << "\n";
    this->person_info();
    cout << score << "\n";
}

test.cpp

# include <iostream>
#include "PS.h"
using namespace std;

int main(){
    Person p = Person("dctwan", 24);	//基类
    Student s = Student("hhh", 20, 100);	//派生类
    p.person_info();	
    s.person_info();	//派生类调用基类方法
    s.stu_info();		//派生类调用自己的方法
    return 0;
}

编译运行

g++ PS.cpp test.cpp

基类指针指向派生类对象

Student s = Student("hhh", 20, 100);
Person *r = &s;     //基类指针指向派生类对象,反之不行
r->person_info();   //基类指针只能调用基类方法

//r->stu_info();	错误使用

多态

同一个方法在基类和派生类中的行为是不同的,换句话说:根据调用函数的对象的类型来执行不同的函数。有两种方法实现多态

  1. 在派生类中重新定义基类方法
  2. 使用虚方法

方法一:在派生类中重新定义基类方法

此方法会根据指针类型来选择方法,以求矩形和三角形面积为例

Shape.h

class Shape{
    protected:
        int height, weight;
    public:
        Shape(int h, int w);
        double area();
};

class Rectangle: public Shape{
    public:
        Rectangle(int h, int w);
        double area();
};

class Trangle: public Shape{
    public:
        Trangle(int h, int w);
        double area();
};

Shape.cpp

#include <iostream>
#include "Shape.h"
using namespace std;

Shape::Shape(int h, int w){
    height = h;
    weight = w;
}

double Shape::area(){
    cout << "Base Class Area" << "\n";
}

Rectangle::Rectangle(int h, int w) : Shape(h, w){}

double Rectangle::area(){
    return height * weight;
}

Trangle::Trangle(int h, int w) : Shape(h, w){}

double Trangle::area(){
    return 0.5 * height * weight;
}

test.cpp

#include <iostream>
#include "Shape.h"
using namespace std;

int main(){
    Rectangle r = Rectangle(2, 3);
    Trangle t = Trangle(2, 3);
    Shape *shape_r = new Rectangle(2, 3);
    Shape *shape_t = new Trangle(2, 3);
    //使用基类指针,调用的是基类的方法
    cout << "shape_r->Rectangle(2,3) area: " << shape_r->area() << "\t shape_t->Trangle(2,3) area: " << shape_t->area() <<"\n";
    //调用相应派生类的方法 
    cout << "Rectangle(2,3) area: " << r.area() << "\t Trangle(2,3) area: " << t.area() << "\n";
    return 0;
}

编译运行

g++ Shape.h Shape.cpp test.cpp

结果如下

shape_r->Rectangle(2,3) area: Base Class Area0   shape_t->Trangle(2,3) area: Base Class Area0
Rectangle(2,3) area: 6   Trangle(2,3) area: 3

方法二:使用虚方法

如果要在派生类中重新定义基类的方法, 通常应将基类方法声明为虚的。 这样, 程序将根据对象类型而不是引用或指针的类型来选择方法版本。 为基类声明一个虚析构函数也是一种惯例。

为何基类需要声明一个虚析构函数?

如果析构函数不是虚的, 则将只调用对应于指针类型的析构函数。 如果析构函数是虚的, 将调用相应对象类型的析构函数。

virtual关键字只在类声明时使用(在Shape.h中使用),方法实现中不使用virtual关键字,且基类和派生类中多态方法都要使用virtual关键字

Shape.h

class Shape{
    protected:
        int height, weight;
    public:
        Shape(int h, int w);
    	~Shape(){}	//虚析构函数
        virtual double area();
};

class Rectangle: public Shape{
    public:
        Rectangle(int h, int w);
        virtual double area();
};

class Trangle: public Shape{
    public:
        Trangle(int h, int w);
        virtual double area();
};

重新编译后运行结果如下

shape_r->Rectangle(2,3) area: 6  shape_t->Trangle(2,3) area: 3
Rectangle(2,3) area: 6   Trangle(2,3) area: 3

抽象基类

在原型中使用=0指出类是一个抽象基类, 在基类中可以不定义该函数的实现。

纯虚函数,相当于接口,由派生类演绎

修改Shape.h,将基类中的area定义为纯虚函数

virtual double area() = 0;

则在Shape.cpp中就可以删除基类中对area的函数实现,如果不定义纯虚函数就删除,编译会报错

double Shape::area(){
    cout << "Base Class Area";
    return 0;
}

运行效果同上