类和对象(继承)
继承
C++面向对象具有三大特点:封装、继承、多态。
不难发现的是,有些类的成员变量和成员函数与其他类的成员变量和成员函数有很多相似的地方,这时候就可以使用继承来减少代码量。
例如,马和驴同属于动物,他们呢又可以生出来骡子,具有马和驴的属性。
继承的基本写法:
class 子类名:继承方式 父类名
{
//子类的成员变量和成员函数
}
继承方式有三种:
私有继承,公共继承和保护继承。
分为以下三种情况(内容代表在子类中的访问权限):
继承方式 | 父类的private | 父类的public | 父类的protected |
---|---|---|---|
私有继承 | 不可访问 | private | private |
公共继承 | 不可访问 | public | protected |
保护继承 | 不可访问 | protected | protected |
注意:父类中的private成员变量和成员函数在子类中都是不可访问的。但是private会被继承下去
私有继承
私有继承的写法:
class 子类名:private 父类名
{
//子类的成员变量和成员函数
}
私有继承的特点是:子类中的成员变量和成员函数不能直接访问父类的成员变量和成员函数,但是可以通过父类的公共成员函数来访问。
例如:
//3-9
#include<iostream>
using namespace std;
class example1
{
public:
int a=1;
protected:
int b=2;
private:
int c=3;
};
class example2 :private example1
{
public:
void print()
{
cout<<a<<endl;
cout<<b<<endl;
//cout<<c<<endl;//父类中的private成员变量和成员函数在子类中都是不可访问的
}
};
int main()
{
example2 ex;
//cout<<ex.a<<endl;这显然是不可以的,因为私有继承,这个访问权限是private的
//cout<<ex.b<<endl;同理,这个访问权限也是private的
ex.print();
}
所以当私有继承的时候,子类中的成员函数不能直接访问父类的成员变量和成员函数,但是可以通过父类的公共成员函数来访问。
公共继承
公共继承的写法:
class 子类名:public 父类名
{
//子类的成员变量和成员函数
}
公共继承的特点是:子类中的成员变量和成员函数可以直接访问父类的成员变量和成员函数。
就不举例子了,和父类的所有访问权限都相同(仅限于public和protected)
保护继承
保护继承的写法:
class 子类名:protected 父类名
{
//子类的成员变量和成员函数
}
保护继承的特点是:子类中的成员变量和成员函数可以直接访问父类的成员变量和成员函数,但是不能通过子类的对象来访问。
例如:
//3-10
#include<iostream>
using namespace std;
class example1
{
public:
int a=1;
protected:
int b=2;
private:
int c=3;
};
class example2 :protected example1
{
public:
void print()
{
cout<<a<<endl;
cout<<b<<endl;
//cout<<c<<endl;//父类中的private成员变量和成员函数在子类中都是不可访问的
}
};
class example3 :protected example2
{
public:
void print()
{
cout<<a<<endl;
cout<<b<<endl;
//cout<<c<<endl;//父类中的private成员变量和成员函数在子类中都是不可访问的
}
};
int main()
{
example2 ex;
//cout<<ex.a<<endl;//这显然是不可以的,因为私有继承,这个访问权限是protected的
//cout<<ex.b<<endl;//同理,这个访问权限也是protected的
//如example3 所示,protected继承,子类中的成员函数可以访问父类中的protected成员变量和成员函数
ex.print();
example3 ex2;
ex2.print();//example3也可以访问到a,b说明protected继承的子类访问权限是protected
}
注意:private权限虽然不可访问,但是会被继承下去。
可以采用visual studio的命令行参数来查看类的内存布局,例如:
cl /d1 reportSingleClassLayout类名 文件名
继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
同名处理:
- 子类对象可以直接访问到子类中同名成员
- 子类对象加作用域可以访问到父类同名成员
- 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数
- 静态成员和非静态成员出现同名,处理方式一致。
- 规范命名。
多继承
多继承的写法:
class 子类名:继承方式1 父类名1,继承方式2 父类名2
{
//子类的成员变量和成员函数
}
多继承可能会引发父类中有同名成员出现,需要加作用域区分
C++实际开发中不建议用多继承
虚继承
虚继承的写法:
class 子类名:virtual 继承方式 父类名
{
//子类的成员变量和成员函数
}
虚继承的特点是:解决多继承中父类中有同名成员出现的问题。
例如:
//3-11
#include<iostream>
using namespace std;
class example1
{
public:
int a=1;
protected:
int b=2;
private:
int c=3;
};
class example2 :virtual public example1
{
public:
void print()
{
cout<<a<<endl;
cout<<b<<endl;
//cout<<c<<endl;//父类中的private成员变量和成员函数在子类中都是不可访问的
}
};
class example3 :virtual public example1
{
public:
void print()
{
cout<<a<<endl;
cout<<b<<endl;
//cout<<c<<endl;//父类中的private成员变量和成员函数在子类中都是不可访问的
}
};
class example4 :public example2,public example3
{
public:
void print()
{
cout<<a<<endl;
cout<<b<<endl;
//cout<<c<<endl;//父类中的private成员变量和成员函数在子类中都是不可访问的
}
};
int main()
{
example4 ex;
ex.print();
}
可以看到,example4中的a,b都是1,2,说明虚继承解决了多继承中父类中有同名成员出现的问题。
虚继承可以解决菱形继承的问题,菱形继承的代码如下:
#include<iostream>
using namespace std;
class example
{
public:
int m_Age=100;
};
class example2 :virtual public example
{
public:
int m_Age=102;
};
class example3:virtual public example
{
int m_age=103;
};
class example4:virtual public example2,virtual public example3
{
public:
void print()
{
cout<<example::m_Age<<endl;
cout<<example2::m_Age<<endl;
cout<<example3::m_Age<<endl;
cout<<example4::m_Age<<endl;
}
};
int main()
{
example4 e;
e.print();
return 0;
}
100
102
100
102
这个代码在visual studio中的对象布局如下: