c++面向对象复习
C++复习总结
面向对象程序的特点
-
抽象性:抽取本质特性(共性),加一简单描述。抽象是面向对象方法的核心
-
封装性:内部细节对外隐藏,通过接口进行控制
- 数据和行为的包装
- 信息隐藏
-
继承性:复用共性,简化描述
- 一般类:包含共性;
- 特殊类:在一般类的基础上,增添每个具体类的个性;
- 继承:特殊类的对象拥有一般类的全部属性和操作;
- 多继承:一个类可以继承多个一般类的特性。
-
多态性:一个接口,多种方式。多态性也是面向对象程序设计的重要特性之一
- 静态多态性:在编译过程中确定同名操作的具体对象
- 在程序运行过程中才确定操作所针对的具体对象
-
联编:把一条消息和一个对象的方法相结合的过程,即调用成员函数
- 消息:(对类的成员函数的)调用
- 方法:成员函数
- 静态联编:在编译阶段完成联编
- 动态联编:在程序运行阶段完成联编
类和对象
类
格式:
class //<类名>
{
private:
//<私有成员函数和数据成员的说明>
protected:
//<保护成员函数和数据成员的说明>
public:
//<公有成员函数和数据成员的说明>
};
举个例子:
//CTime.hpp
class CTime{
private:
int hour;
int minute;
int second;
public:
void setTime(int hour = 0,int minute = 0,int second = 0);
CTime(int hour = 0,int minute = 0,int second = 0);
friend ostream& operator<<(ostream& os,const CTime& time){
os << setw(2) << stefill('0') << time.hour << ":";
os << setw(2) << stefill('0') << time.minute << ":";
os << setw(2) << stefill('0') << time.second;
return os;
}
};
//CTime.cpp
void CTime::setTime(int hour,int minute,int second){
hour = (h>=0 && h<=24>) ? h : 0;
minute = (m>=0 && m<=60>) ? m : 0;
second = (s>=0 && s<=60>) ? s : 0;
}
CTime::CTime(int hour,int minute,int second){
this->setTime(hour,minute,second);
}
void main(){
CTime t1(8,10,1),t2(11,30,1);
CTime* pTime = &t1;//指针指向t1
cout << t1 << endl;
cout << t2 << endl;
cout << *pTime << endl;
}
输出:
08:10:01
11:30:01
08:10:01
成员函数
对象的行为抽象为成员函数,又称为方法
数据成员
对象的属性抽象为数据成员,又称成员变量
对象
类和对象的关系相当于普通数据类型与其变量的关系。
格式:
<对象名>.<公有数据成员名>
<对象名>.<公有成员函数名(实参表)>
举个例子:
rectangle r1(10, 10, 20, 20);
r1.area( );
注意:只有用public定义的公有成员才能使用圆点操作符访问
构造函数和析构函数
//CTime.hpp
CTime(int hour = 0,int minute = 0,int second = 0);
~CTime();
//CTime.hpp
CTime::CTime(int hour,int minute,int second){
this->setTime(hour,minute,second);
cout << *this << ",constrctor..." << endl;
}
CTime::~CTime(){
cout << *this << ",destrctor..." << endl;
}
int main(){
CTime t1(8,20),t2(11,30);
CTime* pTime = &t1;//指针指向t1
cout << t1 << endl;
cout << t2 << endl;
cout << *pTime << endl;
}
输出:
08:20:00,constrctor...
11:30:00,constrctor...
08:20:00
11:30:00
08:20:00
11:30:00,destrctor...
08:20:00,destrctor...
构造函数的两种方法
//推荐
CTime::CTime(int hour,int minute,int second){
this->setTime(hour,minute,second);
cout << *this << ",constrctor..." << endl;
}
//第二种方法
CTime::CTime(int hour,int minute,int second):hour(hour),minute(minute),second(second){}
拷贝构造函数
若没定义拷贝构造函数时,系统默认浅拷贝,存在潜在危险,即当数据类型为指针类型的时候,两个对象的指针会指向同一内存区域。
拷贝构造函数调用场合:
- 用对象初始化对象
- 函数参数传递(此时参数为一个对象)
举个例子:
CTime::CTime(CTime& t1){
this->hour = t1.hour;
this->minute = t1.minute;
this->second = t1.second;
}
例1(构造函数、析构函数和拷贝构造函数)
CTime::CTime(int hour,int minute,int second){
this->setTime(hour,minute,second);
cout << *this << ",constrctor..." << endl;
}
CTime::~CTime(){
cout << *this << ",destrctor..." << endl;
}
CTime::CTime(CTime& t1){
this->hour = t1.hour;
this->minute = t1.minute;
this->second = t1.second;
cout << *this << ",copy constrctor..." << endl;
}
void demo_copy_constryctor(CTime t1){
cout << t1 << ",demo_copy_constrctor..." << endl;
}
int main(){
CTime t1(8);
CTime t2 = t1;
t2.setTime(11,30);
cout << "t2:" << t2 <<endl;
demo_copy_constryctor(t2);
}
输出
08:00:00,constrctor... //t1初始化
08:00:00,copy constrctor... //用t1初始化t2
t2:11:30:00 //cout << "t2:" << t2 <<endl;
11:30:00,copy constrctor... //调用函数,在函数内部初始化形参t1,用t2初始化t1
11:30:00,demo_copy_constrctor... //demo_copy_constryctor(t2);
11:30:00,destrctor... //函数调用完,局部变量t1被调用完释放
11:30:00,destrctor... //函数执行完,释放t2,栈,先释放后入栈的
08:00:00,destrctor... //释放t1
例2(没有拷贝构造函数的风险)
class CDog{
public:
char* name;
CDog(char* name);
friend ostream& operator<<(ostream& os,const CDog& dog1){
os << "I'am a dog, name is " << dog1.name;
return os;
}
};
CDog::CDog(char* name){
this->name = name;
}
void main(){
using namespace N2_CDOG;
char name1[] = "Lao Hei";
CDog dog1(name1) ,dog2 = dog1;
dog2.name[0] = 'H';
cout << "dog1:" << dog1 << endl;
cout << "dog2:" << dog2 << endl;
}
输出
dog1:I'am a dog, name is Hao Hei
dog2:I'am a dog, name is Hao Hei
分析:因为CDog dog1(name1),name1的值为数组的地址,dog2 = dog1,dog1.name和dog2.name指向同一个内存空间,所以dog1和dog2的name都同时被修改
this指针
this指针是指向当前对象的特殊指针。
每个非静态的成员函数都有一个this指针函数的参数,当通过一个对象调用成员函数时,编译器将把当前对象的地址传递给this指针。
//我们的代码
void CTime::showTime(){
cout << hour << ':' << minute << ':' << second << endl;
}
//编译器处理的代码
void CTime::showTime(CTime* const this){
cout << this->hour << ':' << this->minute << ':' << this->second << endl;
}
int main(){
CTime t1;
t1.showTime();//我们的代码
t1.showTime(&t1);//编译器处理的代码
}
静态成员
静态成员属于类,而不属于任何一个对象
私有静态数据成员只能在类内引用,公有或保护静态数据成员可以在类外通过类名引用。
静态数据成员
静态数据成员必须要在类外进行初始化
声明:
static <数据类型> <静态成员名>
初始化:
<类型标识符> <类名>::<静态数据成员名>=<值>
访问:
通过对象访问:person.count
通过类名访问:CPerson::count(推荐)
静态成员函数
访问静态数据成员
区别非静态成员函数,静态成员函数没有this指针,因为类的静态成员函数只有一个运行实例
静态函数成员可以直接引用该类的静态成员,但不能直接引用非静态数据成员
公有静态函数成员可以通过类名或对象名来调用
class CPerson{
private:
char m_strNmae[20];
long m_ID;
double m_wage;
static int m_nCount;//静态成员变量,表示一创建对象的数量
static double m_nTotalWage;//所有雇员的工资总额
public:
CPerson(const char* strName = nullptr, long ID = 0, double wage = 0);
friend ostream& operator<<(ostream& os,const CPerson& person){
os << "(" << person.m_strName << "," << person.m_ID << "," << person.m_wage;
os << "Count:" << CPerson::m_nCount << ",Total Wage:" << CPerson::m_nTotalWage;
return os;
}
};
int CPerson::m_nCount = 0;
double CPerson::m_nTotalWage = 0;
CPerson::CPerson(const char* strName,long ID,double wage){
strcpy(this->m_nstrName,strName);
this->m_ID = ID;
this->m_wage = wage;
CPerson::m_nCount++;
CPerson::m_nTotalWage += this->m_wage;
}
void main(){
CPerson person1("LiMing",1101051,3000.0);
cout << person1 << endl;
CPerson person2("HanMeimei",1101052,5000,0);
cout << person2 << endl;
}
输出
("LiMing",1101051,3000.0) Count:1,Total Wage:3000
("HanMeimei",1101052,5000,0) Count:2,Total Wage:8000
如果需要修改员工工资?
void CPerson::setWage(double wage){
CPerson::m_ntotalWage += wage - this->m_wage;
this->m_wage = wage;
}
原则:
- 尽量少使用全局性的数据
- 全局性数据的操作尽量单一
组合类
将一个已定义的类的对象作为另一个类的数据成员。
构造顺序:
- 由内而外,先构造内嵌类,再构造组合类
- 内嵌类的构造顺序:按内嵌对象在组合类中的定义顺序
<类名>::<类名>(形参表):对象成员1(形参表),对象成员2(形参表),…
{
//类的初始化程序体
}
析构函数的调用顺序与构造函数相反
拷贝构造函数:
C::C(C &c):a(c.a)
{
...
}
Line::Line(Line &line):start(line.start),end(line.end)
{
...
}
例3(组合类的构造函数、析构函数和拷贝构造函数)
//CPoint.hpp
class CPoint
{
private:
int x, y;
public:
CPoint(int x = 0, int y = 0);
CPoint(const CPoint& p);
~CPoint();
friend ostream& operator<<(ostream& os, const CPoint& p) {
os << "(" << p.x << "," << p.y << ")";
return os;
}
};
//CPoint.cpp
CPoint::CPoint(int x, int y) {
this->x = x;
this->y = y;
cout << *this << ",constryctor..." << endl;
}
CPoint::CPoint(const CPoint& p) {
this->x = p.x;
this->y = p.y;
cout << *this << ",copy,constryctor..." << endl;
}
CPoint::~CPoint() {
cout << *this << ",destryctor..." << endl;
}
//CLine.hpp
class CLine {
private:
CPoint start, end;
public:
//CLine(const CPoint start, const CPoint end);
CLine(const CPoint& start, const CPoint& end);
CLine(const CLine& line);
~CLine();
friend ostream& operator<<(ostream& os, const CLine& l) {
os << l.start << "-->" << l.end;
return os;
}
};
//CLine.cpp
CLine::CLine(const CPoint& start, const CPoint& end) :end(end), start(start) {
cout << *this << ",Line consttructor.." << endl;
}
CLine::CLine(const CLine& line) :start(line.start), end(line.end) {
cout << *this << ",Line copy consttructor.." << endl;
}
CLine::~CLine() {
cout << *this << ",Line desttructor.." << endl;
}
//main
int main() {
CPoint p1, p2(2, 2);
CLine line1(p1, p2), line2 = line1;
cout << "p1:" << p1 << endl;
cout << "p2:" << p2 << endl;
cout << "line1:" << line1 << endl;
cout << "line2:" << line2 << endl;
}
输出
(0,0),constryctor...
(2,2),constryctor...
(0,0),copy,constryctor...
(2,2),copy,constryctor...
(0,0)-->(2,2),Line consttructor..
(0,0),copy,constryctor...
(2,2),copy,constryctor...
(0,0)-->(2,2),Line copy consttructor..
p1:(0,0)
p2:(2,2)
line1:(0,0)-->(2,2)
line2:(0,0)-->(2,2)
(0,0)-->(2,2),Line desttructor..
(2,2),destryctor...
(0,0),destryctor...
(0,0)-->(2,2),Line desttructor..
(2,2),destryctor...
(0,0),destryctor...
(2,2),destryctor...
(0,0),destryctor...
思考:
如果将CLine(const CPoint& start, const CPoint& end);
换成CLine(const CPoint start, const CPoint end);
运行结果?
输出
(0,0),constryctor...
(2,2),constryctor...
(0,0),copy,constryctor...
(2,2),copy,constryctor...
(0,0),copy,constryctor...
(2,2),copy,constryctor...
(0,0)-->(2,2),Line consttructor..
(2,2),destryctor...
(0,0),destryctor...
(0,0),copy,constryctor...
(2,2),copy,constryctor...
(0,0)-->(2,2),Line copy constructor..
p1:(0,0)
p2:(2,2)
line1:(0,0)-->(2,2)
line2:(0,0)-->(2,2)
(0,0)-->(2,2),Line destructor..
(2,2),destryctor...
(0,0),destryctor...
(0,0)-->(2,2),Line destructor..
(2,2),destryctor...
(0,0),destryctor...
(2,2),destryctor...
(0,0),destryctor...
友元
声明:
friend class <友元类名>
如果A为B的友元类,则在B中声明friend class A,则A类的所有成员函数都成为B类的友元函数,都可以访问B类的私有和保护成员。
- 友元类的声明同样可以在类声明中的任何位置
- 友元类的所有成员函数都成为友元函数
- 友元关系不能传递
- 友元关系是单向的
//--------A.hpp--------
class B;//声明B是来自外部
class A{
private:
double x;
public:
A(double x=0);
friend B;
friend void absA(A& objA);
friend ostream& operator<<(ostream& os, const A& objA){
os << "A{x = " << objA.x << "}";
return os;
}
};
//--------A.cpp--------
A::A(double x){
this->x = x;
}
//--------myFun.hpp--------
void absA(A& objA);
//--------myFun.cpp--------
void absA(A& objA){
if(objA.x < 0>){
objA.x = -objA.x;
//一般情况,不能在函数内部直接访问objA.x,因为x为类A的私有成员变量
//但是由于A中声明了absA()函数为A的友元函数,所以可以直接访问私有成员变量
}
}
//--------B.hpp--------
#include "A.hpp"//需要引入A的头文件
class B{
public:
double getX(A& objA);
void setX(A& objA ,double x = 0);
//因为类A中声明了B是A的友元,所以在类B中可以直接访问A的私有成员变量
};
//--------B.cpp--------
double getX(A& objA){
return objA.x;
}
void setX(A& objA ,double x = 0){
objA.x = x;
}
//--------main--------
int main(){
A objA1(-5);
B objB1;
cout << objA1 << endl;
absA(objA1);
cout << objA1 << endl;
objB1.setX(objA1, 100);
cout << "A.x = " << objB1.getX(objA1) << endl;
}
输出
A{x = -5}
A{x = 5}
A.x = 100
常对象和常对象成员
常对象
使用const关键字声明的对象称为常对象。
常对象的声明形式为:
const <类名> <对象名>
<类名> const <对象名>
声明常对象的同时,也要进行初始化,该对象以后不能再被更新。
常成员函数
使用const 关键字声明的函数称为常成员函数。
常成员函数声明的形式为:
<类型标识符> <函数名>(参数表) const;
常成员函数不能更新对象的数据成员,也不能调用该类中没有用const修饰的成员函数。
继承与派生
派生类的一般声明语法为:
class <派生类名>:[继承方式] <基类名>
{
//派生类成员声明;
}
其中,继承方式关键字为private、public和protected,系统的默认值为私有继承(private)。
public:基类成员的访问权限在派生类中保持不变
private:基类成员所有公有、保护成员在派生类中成为私有成员
protected:基类成员所有公有、保护成员在派生类中成为保护成员
- 保持已有类的特性而构造新类的过程称为继承
- 在已有类的基础上新增自己的特性而产生新类的过程称为派生
- 被继承的已有类称为基类(或父类)
- 派生出的新类称为派生类(或子类)
举个例子:
class CPoint {
private:
double x,y;
public:
CPoint(double x = 0,double y = 0);
double getX();
double getX();
void setX(double x = 0);
void setY(double y = 0);
};
class CCircle : public CPoint {
private:
double r;
public:
CCircle(double r);
void setR(double r);
void setCenter(double x = 0,double y = 0);
double getR();
double getUpperLeftX();
double getUpperLeftY();
};
void CCircle::setCenter(double x,double y){
this->setX(x);
this->setY(y);
}
double CCircle::getR(){
return this->r;
}
double CCircle::getUpperLeftX(){
return this->getX() - this->r;
}
double CCircle::getUpperLeftY(){
return this->getX() + this->r;
}
int main(){
CCircle c1(100);
c1.setCenter(200,200);
cout << "(" << c1.getX() << "," << c1.getY() << ")";
cout << "R = " << c1.getR() << endl;
cout << "UpperLeft(" << c1.getUpperLeftX() << "," << c1.getUpperLeftY() << ")" << endl;
}
输出
(200,200) R = 100
UpperLeft(100,300)
注意:在本例中,组合类的方式优于继承的方式
同名覆盖
派生类可以对基类的成员重新定义。
举个例子:
class A{
public:
void show(){ cout << "A::show" << endl; }
};
class B:public A{
void show(){ cout << "B::show" << endl; }
void display(){ show(); }
//如何在B的对象中调用A的成员函数show()
//void display(){ A::show(); }
};
int main(){
A a;
B b;
a.show();
b.show();
b.display();
}
输出
A::show
B::show
B::show
派生类的构造函数和析构函数
- 基类的构造函数、析构函数不能被继承,故派生类需要调用基类的构造函数对基类进行实例化。
- 编译时,先生成基类构造函数的调用代码,再生成派生类构造函数的调用代码。
- 基类构造函数的调用:
- 隐式调用:派生类未指定基类构造函数,则调用基类默认的构造函数。
- 显式调用:派生类指定基类构造函数,派生类将参数传递给基类的构造函数。
- 析构过程与构造过程相反。
- 拷贝构造函数:
C::C(C &c1):B(c1){ ... }
构造函数的显示调用
<派生类名>::<派生类名>(<形参声明>):<基类名>(<参数表>)
{
//派生类构造函数的函数体;
}
<形参声明>部分参数传递给基类构造函数- 基类有多个构造函数时,编译器根据
<参数表>来确定调用哪一个基类构造函数
举个例子:
class CPoint{
protected:
int x,y;
public:
CPoint(int x=0,int y=0){
this->x=x;
this->y=y;
cout<<"point constructor:"<<'['<<x<<","<<y<<']'<<endl;
};
~CPoint(){
cout<<"point destructor:"<<'['<<x<<","<<y<<']'<<endl;
};
};
class CCircle{
protected:
int radius;
public:
CCircle(int x=0,int y=,int r=0):CPoint(x,y) {
radius = r;
cout<<"circle constructor:"<<'['<<radius<<']'<<'['<<x<<","<<y<<']'<<endl;
};
~CCircle(){
cout<<"circle destructor:"<<'['<<radius<<']'<<'['<<x<<","<<y<<']'<<endl;
};
};
class CCylinder{
protected:
int height;
public:
CCylinder(int x=0,int y=,int r=0,int h=0):CCylinder(x,y,r) {
height = h;
cout<<"cylinder constructor:"<<'['<<height<<']'<<'['<<radius<<']'<<'['<<x<<","<<y<<']'<<endl;
};
~CCylinder(){
cout<<"cylinder destructor:"<<'['<<radius<<']'<<'['<<x<<","<<y<<']'<<endl;
};
};
int main(){
CCylinder cylinder(200,300,100,400);
}
输出
point constructor:[200,300]
circle constructor:[100][200,300]
cylinder constructor:[400][100][200,300]
cylinder destructor:[400][100][200,300]
circle destructor:[100][200,300]
point destructor:[200,300]
多继承
- 单继承:一个派生类只有一个直接基类
- 多继承一个派生类有多个直接基类
多继承定义形式:
class <派生类名>:[继承方式] 基类名1 ,…, [继承方式] 基类名n
{
派生类成员声明;
};
多继承派生类的构造方法:
<派生类名>(<形参声明>):<基类名1>(<参数表1>) ,…, <基类名n>(<参数表n>)
{
派生类构造函数体;
};
举个例子:
class Base1{
private:
int a;
public:
Base1(int x){
a=x;
cout<<"Base1 Constructor!"<<endl;
}
int getA(){ retrun a; }
};
class Base2{
private:
int b;
public:
Base2(int x){
b=x;
cout<<"Base2 Constructor!"<<endl;
}
int getB(){ retrun b; }
};
//定义顺序决定基类构造函数的调用顺序
class Deriver : public Base1, public Base2{
private:
int c;
public:
Derived(int x,int y,int z) : Base2(z), Base1(y) {
c=x;
cout<<"Derived Constructor!"<<endl;
}
void show(){
cout<<getA()<<','<<getB()<<','<<c<<endl;
}
};
int main(){
Derived obj(1,2,3);
obj.show();
}
输出
Base1 Constructor!
Base2 Constructor!
Derived Constructor!
2,3,1
虚基类
二义性问题的产生:被继承的基类有同名函数
class Base1{
public:
int a;
void set(int a){ this->a = a; }
};
class Base2{
public:
int a;
void set(int a){ this->a = a; }
};
//定义顺序决定基类构造函数的调用顺序
class MultiDeri : public Base1, public Base2{
public:
int get(){ return this->a; }
};
int main(){
Derived obj(1,2,3);
obj.show();
}
ERROR!!!
二义性问题(菱形缺陷):
父类没有同名成员,但有共同的爷类
class A{
public:
int a;
};
class B : public A{
public:
int b;
};
class C : public A{
public:
int c;
};
class D : public B,public C{
public:
int d;
};
解决方式:
- 作用域限定符,d1.B::a,d1.C::a。
- 虚基类派生,对同名成员只保留一个副本。虚基类是一种派生方式,不是一种类。
虚基类及其构造函数的调用
- 基类名称前加virtual即为虚基类
- 调用虚基类的派生类的构造函数时,首先要调用虚基类的构造函数
- 在多层次继承中,虚基类的构造函数只能调用一次
- 虚基类的构造函数由最派生类(创建对象的类)调用,最派生类的非虚基类对虚基类的构造函数的调用将被忽略
- 隐式调用,最派生类自动调用虚基类的默认构造函数
- 若虚基类没有默认构造函数,虚基类的所有直接或间接派生类都必须显式调用
class A{
private:
int a;
public:
A(int a): a(a){
cout<<"A constructor:"<<a<<endl;
}
~A(){
cout<<"A destroyed."<<endl;
}
};
class B1 : public virtual A{
private:
int b1;
public:
B1(int a,int b1): A(a){
this->b1=b1;
cout<<"B1 constructor:"<<b1<<endl;
}
~B1(){
cout<<"B1 destroyed."<<endl;
}
};
class B2 : public virtual A{
private:
int b2;
public:
B2(int a,int b2): A(a){
this->b2=b2;
cout<<"B2 constructor:"<<b2<<endl;
}
~B1(){
cout<<"B2 destroyed."<<endl;
}
};
class C : public B1,public B2{
public:
int c;
C(int a,int b1,int b2,int c) : B1(a,b1),B2(a,b2),A(a){
this->c=c;
cout<<"C constructor:"<<c<<endl;
}
~C(){
cout<<"C destroyed."<<endl;
}
};
int main(){
C c1(1,2,3,4);
}
输出:
A constructor:1
B1 constructor:2
B2 constructor:3
C constructor:4
C destroyed.
B2 destroyed.
B1 destroyed.
A destroyed.
多态与虚函数
- 静态多态(编译时多态):通过重载实现
- 动态多态(运行时多态):通过虚函数实现
基类指针指向派生类对象
- c++允许一个基类对象的指针指向其派生类的对象,但不允许派生类对象的指针指向其基类对象。
- 将一个基类对象的指针指向其派生类的对象,通过该指针只能访问派生类中从基类继承的共有成员,不能访问派生类中新增的成员,除非通过强制类型转换将基类指针转换为派生类指针。
class A {
private:
int a;
public:
void setA(int i) { a = i; }
void showA() { cout << "a = " << a << endl; }
};
class B :public A {
private:
int b;
public:
void setB(int i) { b = i; }
void showB() { cout << "B = " << b << endl; }
};
int main() {
A a, * pa;
B b, * pb;
//通过基类指针pa访问B中从A继承的公有成员
pa->setA(100);
pa->showA();
pb = (B*)pa;//积累指针强制转化为派生类指针
//不能通过基类指针pa访问派生类自己定义的成员
pb->setB(200);
pb->showB();
//以下语句错误
//pb = &a;//不能将 "A *" 类型的值分配到 "B *" 类型的实体
//pa->setB(50);//类 "A" 没有成员 "setB"
//pa->showB();//类 "A" 没有成员 "showB"
}
输出
a = 100
b = 200
原理解析
思路:用基类指针指向派生类对象,通过基类指针来统一调用不同派生类的成员,从而实现多态。
问题:这样只能调用基类成员,不能调用派生类成员。
A a, *pa;//pa为基类指针
B b, *pb;//pb为派生类指针
pa = &b;//基类指针pa指向派生类对象b
pb = (B*)pa;//基类指针签字转化为派生类指针
pa = &a;//错误:派生类只能不能指向基类
虚函数
- 基类指针指向派生类对象,只能调用基类成员。
- 基类指针指向派生类对象,通过虚函数,可调用派生类成员。
- 此时,多态形成,运行时多态,动态多态。
- 同一个指针,可调用不同对象的同名方法,得到不同相应,对同一消息做出不同相应。
- 为同一类继承结构中所有类的同一行为提供统一接口。
- 虚函数声明形式如下:
virtual<函数类型><函数名>(<形成声明>);- 声明为虚函数,意味着该成员函数在派生类中可能被重新定义。
class A {
public:
virtual void show() { cout << "A::show" << endl; }
};
class B :public A {
public:
void show() { cout << "B::show" << endl; }
};
class C :public A {
public:
void show() { cout << "C::show" << endl; }
};
int main() {
A a, * pa;
B b;
C c;
pa = &a; pa->show();
pa = &b; pa->show();
pa = &c; pa->show();
}
输出
A::show
B::show
C::show
class CAnimal {
public:
virtual void run() {}
};
class CPerson :public CAnimal {
public:
void run() { cout << "Person:Run on 2 legs." << endl; }
};
class CDog :public CAnimal {
public:
void run() { cout << "Dog:Run on 4 legs." << endl; }
};
class CBug :public CAnimal {
public:
void run() { cout << "Person:Run on 6 legs." << endl; }
};
void animalRun(CAnimal* pa) {
pa->run();
}
int main() {
CPerson person1;
CDog dog1;
CBug bug1;
animalRun(person1);
animalRun(dog1);
animalRun(bug1);
}
输出
Person:Run on 2 legs.
Dog:Run on 4 legs.
Person:Run on 6 legs.
虚构造函数、虚析构函数
- 构造函数不能被定义为虚函数,虚函数需要通过对应的虚指针vtable来调用。
- 析构函数建议全部定义为虚函数,释放对象时,只有定义为虚析构函数才能保证先释放派生类再释放基类。
class Base {
public:
Base() { cout << "Base constructor." << endl; }
virtual ~Base() { cout << "Base destructor." << endl; }
};
class Derived {
public:
Derived() { cout << "Derived constructor." << endl; }
virtual ~Derived() { cout << "Derived destructor." << endl; }
};
int main() {
Base* pBase = new Derived;
delete pBase;
}
输出
Base constructor.
Derived constructor.
Derived destructor.
Base destructor.
纯虚函数与抽象类
- 有时在基类中无法给出基类中虚函数的实现代码,这时可以把虚函数声明为纯虚函数,
virtual<函数类型><函数名>(<形参声明>)=0;
- 纯虚函数没有函数体,及提供一个统一的接口。
- 含有纯虚函数的类称为抽象类,抽象类不能实例化。
class CAnimal{
virtual void run() = 0;
};
class CShape {
protected:
double s;
public:
CShape() { s = 0; }
virtual double area() = 0;
};
class CCircle :public CShape {
private:
double r;
public:
CCircle(double r) { this->r = r; }
double area() { return 3.14 * r * r; }
};
int main() {
CShape* pShape;
CCircle circle(3);
pShape = &circle;
cout << "area = " << pShape->area() << endl;
}
纯虚函数与抽象类语法规则
- 若必须定义为抽象类,而不合适的函数可作为纯虚函数,可将析构函数定义为纯虚函数。
- 为了通过编译,必须在内外实现该纯虚函数。(有的编译器不用实现)
class CShape {
protected:
double s;
public:
CShape() { s = 0; }
virtual double area() { return 0; }
virtual ~CShape() = 0;
};
CShape::~CShape() {};
重载
int my_abs(int val) {
return (val < 0) ? -val : val;
}
int my_abs(float val) {
return (val < 0) ? -val : val;
}
int main() {
int i = 100;
float f = -125.78f;
cout << my_abs(i) << endl;
cout << my_abs(f) << endl;
}
输出
100
125.78
构造函数重载
class Box {
private:
double height, width, depth;
public:
Box();
Box(double h);
Box(double h, double w);
Box(double h, double w, double d);
//超级构造函数
//Box(double h = 0, double w = 0, double d = 0);
};
int main() {
Box box1;
Box box2(1.0);
Box box3(1.0, 2.0);
Box box4(1.0, 2.0, 3.0);
}
重载规则
- 重载:函数名相同,参数类型不同
- 以下重载非法:
- 返回类型不同:
long fun(int);float fun(int);
- 不能利用引用重载
void fun(int&);void fun(int);
- 返回类型不同:
- const可用于重载,以下合法:
void fun();void fun() const;
运算符重载
运算符重载的一般形式:
<函数类型>operator<运算符>(<形参表>);
{
//<函数体>
}
运算符函数原型:
- 输出流运算符函数原型
ostream& operator<<(ostream& os, <操作对象>)
- 加法运算符函数原型
<返回类型> operator+(<操作对象>,<操作对象>)
- 自增运算符函数原型
- 前缀:
<返回类型> operator++(<操作对象>) - 后缀:
<返回类型> operator++(<操作对象>,int)
- 前缀:
- 下标运算符函数原型
<返回类型>& operator[](<操作对象>,int)
普通形式函数形式重载
class CComplex {
private:
double r, i;
public:
CComplex(double r = 0, double i = 0);
virtual ~CComplex();
friend CComplex operator+(CComplex c1, CComplex c2);
//...输出流重载
};
CComplex operator+(CComplex c1, CComplex c2) {
CComplex CTemp;
CTemp.r = c1.r + c2.r;
CTemp.i = c1.i + c2.i;
return CTemp;
}
int main() {
CComplex c1(1, 1), c2(2, 2);
CComplex c3 = c1 + c2;
cout << c1 << endl;
cout << c2 << endl;
cout << c3 << endl;
}
输出
(1,1)
(2,2)
(3,3)
成员函数形式重载
class CComplex1 {
private:
double r, i;
public:
CComplex1(double r = 0, double i = 0);
virtual ~CComplex1();
CComplex1 operator+(CComplex1 c2);
//...输出流重载
};
CComplex1 CComplex1::operator+(CComplex1 c2) {
//实际:CComplex1 CComplex1::operator+(CComplex1* const this, CComplex1 c2)
CComplex1 CTemp;
CTemp.r = this->r + c2.r;
CTemp.i = this->i + c2.i;
return CTemp;
}
int main() {
CComplex1 c1(1, 1), c2(0.1, 0.1);
CComplex1 c3 = c1 + c2;
cout << c1 << endl;
cout << c2 << endl;
cout << c3 << endl;
}
输出
(1,1)
(0.1,0.1)
(1.1,1.1)
自增运算符重载
class Counter {
private:
int value;
public:
Counter() { value = 0; }
Counter operator++();//前缀运算符
Counter operator++(int);//后缀运算符
void display() {
cout << "value:" << value << endl;
}
};
Counter Counter::operator++() {
value++;
return *this;
}
Counter Counter::operator++(int) {
Counter temp;
temp.value = this->value++;
return temp;
}
int main() {
Counter obj1, obj2;
obj2 = obj1++;
obj1.display();
obj2.display();
obj2 = ++obj1;
obj1.display();
obj2.display();
}
输出
value:1
value:0
value:2
value:2
下标运算符重载
class Interger {
private:
int* array;
int len;
public:
Interger(int len) {
this->len = len;
array = new int[len];
}
int& operator[](int i);//重载运算符[]
~Interger() { delete[]array; }
};
int& Interger:: operator[](int i) {
if (i<0 || i>len - 1) {
cout << "错误:数据越界!" << endl;
}
return array[i];
}
int main() {
Interger array(10);
int i = 0;
for (i = 0; i < 10; i++) {
array[i] = i + 1;
cout << array[i] << " ";
}
cout << ":i = " << i << endl;
cout << array[i] << endl;
}
输出
1 2 3 4 5 6 7 8 9 10 : i = 10
错误:数据越界!

浙公网安备 33010602011771号