1-2 友元、运算符重载和继承
1 友元
目的是让一个函数或者类可以访问另一个类的私有成员,其关键字为friend
三种实现:
- 全局函数做友元
在类外定义一个全局函数,在类内的最上方将其声明为友元函数,声明的时候不需要放在public或private中,如:
friend void goodGay(Building &building);
- 类做友元
将允许访问本类私有属性的类在最上面声明为friend
friend class goodGay;
- 成员函数做友元
将某一类允许访问本类私有属性的成员函数在最上面声明为friend
friend void GoodGay::visit();
2 运算符重载
对已有运算符重新定义,赋予其另一种功能,以适应不同的数据类型,比如两个对象实现相加
1 重载加号运算符+
成员函数重载,这个时候调用者就是符号左边的对象
class Person
{
public:
int m_A;
int m_B;
Person operator+(Person &p)
{
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}
};
调用时直接:
Person p3 = p1 + p2;//p1.operator+(p2);
全局函数重载,这个时候它是作为一个函数来使用的,不属于任何对象,传入参数时有要求,第一个参数就是运算符左边的参数,第二个参数就是运算符右边的参数
Person operator+(Person &p1, Person &p2)
{
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
Person p3 = p1 + p2;//operator+(p1,p2);
2 重载输出运算符<<
使得能够输出任意类型,比如可以直接对某一对象的属性进行输出,比如cout << p;
要注意在这里是无法进行成员函数调用的,调用者是对象,而输出的时候相当于是cout调用了<<,只能进行全局函数的重载
ostream &operator<<(ostream &cout, Person &p)//流对象不能拷贝,只能地址传递
{
cout<<p.m_A<<" "<<p.m_B<<endl;
return cout;
}
为满足链式编程思想,即<<可重复调用,返回值应该设置为cout类型
3 重载递增运算符++
让对象也可以实现自增
重载前置++
//以引用的形式返回对象本身,因为我们对于一个对象可能会有多次自增操作,引用确保
我们每次操作的是同一个对象,否则就是拷贝形式的返回,返回的是一个新对象
MyInteger &operator++(){
m_NUm++;
return *this;//返回这个对象本身
}
重载后置++
//用一个占位参数来实现函数重载,这里不能返回引用了,因为局部对象不允许返回引用
MyInteger operator++(int){
//先记录自增前的结果
MyInteger temp = *this;
//再递增
m_NUm++;
//返回记录结果
return temp;
}
在进行自增时,我们调用++得到的是返回结果,也就是先前的值,然后再次输出时得到的就是加1的结果
4 重载赋值运算符=
C++编译器对一个类还提供第四个默认函数,就是operator=,其可以进行对象间的浅拷贝,但是如果对象有堆区数据,在释放的时候会进行重复释放,这个时候可以进行重载=运算符
int *m_Age;
Person &operator=(Person &p){
if(m_Age!=NULL){
delet m_Age;//先判断是否有属性在堆区,如果有先释放干净,再深拷贝
m_Age = NULL;
}
m_Age = new int(*p.m_Age);
return *this;
}
同样为了实现连等操作,需要返回对象本身
5 重载关系运算符==
目的是可以实现对象之间的比较
bool operator==(Person &p){
if(this->m_name == p.m_name && this->m_age == p.m_age){
return true;
}
return false;
}
6 重载函数调用运算符()
由于重载之后对象可以像函数一样使用,这样的对象又称为仿函数
class MyPrint
{
public:
//重载函数调用运算符
void operator()(string test){
cout<<test<<endl;
}
};
void test01(){
MyPrint myPrint;//相当于创建了一个仿函数
myPrint("hello");
}
int main()
{
test01();
return 0;
}
匿名函数对象,如MyPrint("hello");
中,MyPrint()操作就相当于创建了一个匿名对象,其特点是当前行执行完后立即释放掉。
3 继承
利用继承的技术可以实现代码的复用
class A : 继承方式 B
A称作子类或派生类,B称作父类或基类
1 继承方式
- 公共继承:父类的公共和保护到了子类还是各自的属性
- 保护继承:父类的公共和保护到了子类全变成了保护属性
- 私有继承:父类的公共和保护到了子类全变成了私有属性
父类私有无论哪一种继承,子类均继承不到,无法访问
2 继承中的对象模型
继承时会继承父类的所有非静态成员(静态只有一份,子类父类共享),只是有些成员访问不到而已,可以通过sizeof来查看子类和父类的大小
3 继承中的构造和析构顺序
父类构造
子类构造
子类析构
父类析构
4 继承中同名成员的处理
对于同名成员的访问:
- 默认是访问子类的成员,子类会隐藏同名的父类成员
- 如果要访问父类成员要加上作用域::
无论是静态还是非静态都一样
5 多继承
C++允许一个类继承多个类,语法:
class 子类:继承方式 父类1,继承方式,父类2,...
实际开发中不建议多继承
6 菱形继承
两个派生类继承同一个基类,又有一个类同时继承两个派生类,这种继承方式被称作菱形继承
这种方式有两个缺点:
- 二义性,如果两个派生类有同名函数,那么继承过来使用二义
- 两个派生类中从基类继承来的成员变量在最终的类中无法区分,只能加以作用域来区分
解决方法:虚继承
class animal{};//该类被称为虚基类
class sheep:virtual public animal{};//虚继承
class tuo:virtual public animal{};//虚继承
class sheeptuo:public sheep, public tuo{};
这样在sheeptuo类中就不会再出现两份成员变量了,只存在一份