c++ OOP(2)
运算符重载
重新定义+-*/
操作,对同类对象使用,以时间类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(); 错误使用
多态
同一个方法在基类和派生类中的行为是不同的,换句话说:根据调用函数的对象的类型来执行不同的函数。有两种方法实现多态
- 在派生类中重新定义基类方法
- 使用虚方法
方法一:在派生类中重新定义基类方法
此方法会根据指针类型来选择方法,以求矩形和三角形面积为例
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;
}
运行效果同上