动态多态
基于虚函数和继承实现动态多态。
动态多态需要满足三个条件:
基类中定义了虚函数。
子类中重定义(覆盖了、重写了)了基类的虚函数。
用基类指针指向子类对象且调用该虚函数。
class Person
{
public:
/*
定义虚函数
子类覆盖父类的虚函数后,父类指针指向子类对象时,调用子类的函数。
*/
virtual void buyTicket()
{
cout << "普通人买全票" << endl;
}
};
class Student : public Person
{
public:
// 子类覆盖父类的方法
void buyTicket()
{
cout << "学生买学生票" << endl;
}
};
class Chilren : public Person
{
public:
// 子类覆盖父类的方法
void buyTicket()
{
cout << "6岁以下免票" << endl;
}
};
class Soldier : public Person
{
public:
void buyTicket()
{
cout << "军人免票" << endl;
}
};
int main()
{
Person person;
Student student;
Chilren chilren;
Soldier soldier;
// 父类指针指向子类对象
Person* p;
p = &student;
// 父类指针访问子类对象的buyTicket方法
p->buyTicket();
// 父类指针指向另一子类,并调用该子类的buyTicket方法。
p = &chilren;
p->buyTicket();
Person* p1[4] = { &student, &person, &chilren, &soldier };
for (int i = 0; i < 4; i++)
{
p1[i]->buyTicket();
}
return 0;
}
虚函数实现动态多态的机制
虚函数的对象所占用的存储空间。
/*
由于包含虚函数类的对象有一个虚指针,与没有虚指针的对象相比,含有虚函数的类的对象所占用内存空间要多。
*/
class A
{
private:
int a;
public :
void func()
{}
};
class B
{
private:
int a;
public:
virtual void func()
{}
};
int main()
{
A a;
B b;
cout << "sizeof(a): " << sizeof(a) << endl;
cout << "sizeof(b): " << sizeof(b) << endl;
return 0;
}
虚析构函数
如果基类的析构函数定为虚函数,则派生类的析构函数就会自动成为虚析构函数。
动态分配创建对象时,通过父类指针释放子类对象,如果父类的析构不是虚析构函数,则仅调用父类析构函数。
class Employee
{
protected:
char m_name[30];
public:
Employee(const char* name)
{
cout << "Employee(const char* name): " << name << endl;
strcpy_s(m_name, 30, name);
}
// 定义虚析构函数
virtual ~Employee()
{
cout << "~Employee(): " << m_name;
}
};
class Teacher : public Employee
{
private:
char *m_course;
public:
Teacher(const char* name, const char* course) : Employee(name)
{
cout << "Teacher(const char* name, const char* course) :" << name << ", " << course << endl;
int len = strlen(course) + 1;
m_course = new char[len];
strcpy_s(m_course, len, course);
}
~Teacher()
{
cout << "~Teacher(): " << m_name << ", " << m_course << endl;
delete []m_course;
}
};
int main()
{
/*
普通方式创建对象时,如果父类的析构不是虚析构函数,
当对象被编译器销毁时,父类和子类的析构都会被调用。
Teacher t1("Jack", "Math");
*/
/*
动态分配创建对象时,通过 子类指针 释放 子类对象,
如果父类的析构不是虚析构函数,父类和子类的析构都会被调用。
Teacher* p1 = new Teacher("Rose", "Chinese");
delete p1;
*/
/*
动态分配创建对象时,通过父类指针释放子类对象,
如果父类的析构不是虚析构函数,则仅调用父类析构函数。
*/
Employee* p2 = new Teacher("Tom", "Computer");
delete p2;
return 0;
}
纯虚函数与抽象类
纯虚函数是不需要定义函数体的特殊函数。
包含纯虚函数的类就是抽象类。
// 包含纯虚函数的类就是抽象类
class Pet
{
public:
// 定义纯虚函数
virtual void barking() = 0;
// 纯虚函数可以被函数成员调用
void play()
{
barking();
cout << "在开开心心的玩" << endl;
}
};
/*
Cat是Pet的派生类,继承了Pet的纯虚函数barking(),
Cat也是包含纯虚函数的类,即Cat也是抽象类。
*/
class Cat : public Pet
{
};
/*
Dog是Pet的派生类,覆盖了Pet的纯虚函数barking(),
Dog不包含任何纯虚函数的类,Dog可以实例化。
*/
class Dog : public Pet
{
public:
void barking()
{
cout << "小狗汪汪叫" << endl;
}
};
class Bird : public Pet
{
public:
void barking()
{
cout << "小鸟喳喳叫" << endl;
}
};
int main()
{
// Pet pet; 抽象类不能实例化
// Cat cat; Cat也是抽象类,不能实例化。
// Dog不包含纯虚函数,可以实例化。
Dog dog;
dog.barking();
// dog调用了父类的play()方法,父类的play()方法又调用了Dog覆盖的barking()方法。
dog.play();
Bird brid;
// bird调用了父类的play()方法,父类的play()方法又调用了Bird覆盖的barking()方法。
brid.play();
}
类的高级特性
深拷贝与浅拷贝
浅拷贝
简单地将参数的属性赋值给新建对象对应的属性,这种赋值被称为浅拷贝。
当类中没有定义拷贝构造函数时,系统会提供一个默认的拷贝构造函数,默认拷贝构造函数就是将参数对象值赋给新对象对应的属性。使用默认拷贝构造的拷贝被称为浅拷贝。
// 当类中没有定义拷贝构造函数时,系统会提供一个默认的拷贝构造函数。
class Student
{
private:
// 当属性是数组时, 默认的拷贝构造函数会拷贝数组的值,此时依然是浅拷贝。
char m_name[20];
int m_no;
public:
Student(const char* name, int no)
{
strcpy_s(m_name, 20, name);
m_no = no;
}
void show()
{
cout << m_name << ", " << &m_name << ", " << m_no << endl;
}
};
int main()
{
Student s1("Tom", 20);
s1.show();
// 使用默认的拷贝构造函数进行浅拷贝。
Student s2(s1);
s2.show();
}
深拷贝
默认拷贝构造不能满足要求时,需要自己定义拷贝构造函数实现深度复制,这种拷贝叫做深拷贝。
class Student
{
private:
char* m_name;
int m_no;
void copy(const char* name, int no)
{
int len = strlen(name) + 1;
m_name = new char[len];
strcpy_s(m_name, len, name);
m_no = no;
}
public:
Student(const char* name, int no)
{
copy(name, no);
}
~Student()
{
delete[] m_name;
}
// 自定义拷贝构造函数
Student(const Student& s)
{
copy(s.m_name, s.m_no);
}
void show()
{
cout << m_name << ", " << &m_name << ", " << m_no << endl;
}
};
int main()
{
Student t1("Tom", 1000);
Student t2(t1);
t1.show();
t2.show();
return 0;
}
类的组合
类的组合就是在一个类中内嵌其他类的对象作为成员。
如果内嵌对象的类有无参构造函数,则在组合类构造函数的初始化表中可以不提供对该内嵌对象的初始化参数;如果没有无参构造函数,则必须在初始化列表中调用有参的构造函数。
如果有多个内嵌对象时,内嵌对象的构造顺序和声明顺序相同。
// Point.h
#pragma once
class Point
{
private:
int m_x;
int m_y;
public:
Point(int x, int y);
Point(const Point& point);
int getX();
int getY();
};
// Point.c
#include "Point.h"
#include <iostream>
using namespace std;
Point::Point(int x, int y)
{
cout << "Point(int x, int y): " << x << ", " << y << endl;
m_x = x;
m_y = y;
}
Point::Point(const Point& point)
{
cout << "Point::Point(const Point& point): " << point.m_x << ", " << point.m_y << endl;
m_x = point.m_x;
m_y = point.m_y;
}
int Point::getX()
{
return m_x;
}
int Point::getY()
{
return m_y;
}
// Circle.h
#pragma once
#include "Point.h"
class Circle
{
private:
const float PI = 3.14159;
Point m_center; //内嵌
int m_radius;
public:
Circle(Point& center, int radius);
void show();
};
// Circle.c
#include "Circle.h"
#include <iostream>
using namespace std;
/*
内嵌对象初始化:
内嵌对象m_center时Circle的组成部分,在创建Circle对象时,也要创建M_center并对其进行初始化。
当内嵌对象没有无参构造函数时,需要在初始化列表中调用内嵌对象的构造函数。
C++通过构造函数的初始化列表为内嵌对象初始化。
*/
Circle::Circle(Point& center, int radius) : m_center(center.getX(), center.getY())
/*
对象传参:
形参是Point的引用时,本质上传递实参的地址,不会创建形参对象;
形参是Point类型时,创建形参对象;
行为为引用时Point的拷贝构造函数不会被调用;形参为值时,Point的拷贝构造函数会被调用。
*/
{
m_radius = radius;
}
void Circle::show()
{
cout << "Circle: (x,y)" << m_center.getX() << ", " << m_center.getY() << " radius: " << m_radius << endl;
}
// Triangle.h
#pragma once
#include "Point.h"
/*
多个内嵌对象的构造顺序只和内嵌对象的声明顺序有关。
*/
class Triangle
{
private:
Point m_point2;
Point m_point1;
Point m_point3;
public:
Triangle(Point& point1, Point& point2, Point& point3);
void show();
};
// Triangle.c
#include "Triangle.h"
#include <iostream>
using namespace std;
// 内嵌对象的构造顺序和构造列表中声明的顺序无关
Triangle::Triangle(Point& point1, Point& point2, Point& point3) : m_point3(point3), m_point1(point1), m_point2(point2)
{
// 内嵌对象的构造顺序是先构造内嵌对象,再执行构造函数体。
cout << "Triangle::Triangle(Point& point1, Point& point2, Point& point3)" << endl;
}
void Triangle::show()
{
cout << "Triangle: x1,y1:" << m_point1.getX() << ", " << m_point1.getY() ;
cout << " x2,y2:" << m_point2.getX() << ", " << m_point2.getY();
cout << " x3,y3:" << m_point3.getX() << ", " << m_point3.getY() << endl;
}
//void show();
// 类的组合
#include <iostream>
#include "Circle.h"
#include "Triangle.h"
using namespace std;
int main()
{
Point center(1, 1);
Circle circle(center, 3);
circle.show();
Point center1(10, 10), center2(20, 20), center3(30, 30);
Triangle(center1, center2, center3);
return 0;
}