系列文章目录
文章目录
前言
前面学到了函数的重载,现在又新出现一个叫运算符重载的东西,还有C++三大特性之一的继承又是如何实现,起什么作用的呢?
一、运算符重载
运算符重载的概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
1.加号运算符
作用:实现两个自定义数据类型相加的运算。
有两种实现方式
- 成员函数重载+号
- 全局函数重载+号
1.1成员函数重载+号
//加号运算符重载
class Person
{
public:
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;
}
int m_A;
int m_B;
};
int main()
{
Person p1;
p1.m_A = 10;
p1.m_B = 10;
Person p2;
p2.m_A = 10;
p2.m_B = 10;
Person p3 = p1 + p2;
return 0;
}
1.2全局函数重载+号
//加号运算符重载
class Person
{
public:
int m_A;
int m_B;
};
//全局函数重载加号
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;
}
int main()
{
Person p1;
p1.m_A = 10;
p1.m_B = 10;
Person p2;
p2.m_A = 10;
p2.m_B = 10;
Person p3 = p1 + p2;
cout << "p3.m_A = " << p3.m_A << endl;
return 0;
}
2.左移运算符
作用:可以输出自定义数据类型
左移运算符不能再使用成员函数重载,因为这样会导致重载后的结果变成p << cout,顺序反了,不是我们想要的结果,因此使用全局函数重载<<运算符
//左移运算符重载
class Person
{
public:
Person(int a, int b)
{
m_A = a;
m_B = b;
}
int m_A;
int m_B;
};
//只能利用全局函数重载左移运算符
ostream& operator<<(ostream &cout, Person &p)
{
cout << "m_A = " << p.m_A << " m_B = " << p.m_B;
return cout;
}
int main()
{
Person p(10, 20);
cout << p << endl;//本质 operator<<(cout, p)
return 0;
}
3.递增运算符重载
作用:通过重载递增运算符,实现自己的整型数据
//递增运算符重载
class MyInteger
{
friend ostream &operator<<(ostream &cout, MyInteger myint);
public:
MyInteger()
{
m_Num = 0;
}
//前置++重载
MyInteger &operator++()
{
//先++
m_Num++;
//再返回
return *this;
}
//后置++重载
MyInteger operator++(int)
{
//先返回
MyInteger temp = *this;
//再++
m_Num++;
//再返回
return temp;
}
private:
int m_Num;
};
ostream &operator<<(ostream &cout, MyInteger myint)
{
cout << myint.m_Num;
return cout;
}
int main()
{
MyInteger myint;
cout << ++(++myint) << endl;
cout << myint++ << endl;
return 0;
}
4.赋值运算符
C++编译器至少给一个类添加4个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
- 赋值运算符operator=,对属性进行值拷贝
Tip:如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝的问题
//赋值运算符重载
class Person
{
public:
Person(int age)
{
m_Age = new int(age);
}
~Person()
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
}
//重载赋值运算符
Person& operator=(Person& p)
{
//先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
//深拷贝
m_Age = new int(*p.m_Age);
//返回自身
return *this;
}
int* m_Age;
};
void test01()
{
Person p1(18);
Person p2(20);
Person p3(30);
p3 = p2 = p1;
cout << "p1的年龄为:" << *p1.m_Age << endl;
cout << "p2的年龄为:" << *p2.m_Age << endl;
cout << "p3的年龄为:" << *p3.m_Age << endl;
}
int main()
{
test01();
return 0;
}
5.关系运算符重载
作用:重载关系运算符,可以让两个自定义类型对象进行对比操作
//关系运算符重载
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
bool operator==(Person& p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
return false;
}
bool operator!=(Person& p)
{
if (this->m_Name != p.m_Name || this->m_Age != p.m_Age)
{
return true;
}
return false;
}
string m_Name;
int m_Age;
};
void test01()
{
Person p1("Tom", 18);
Person p2("Tom", 18);
if (p1 == p2)
{
cout << "p1和p2相等" << endl;
}
else
{
cout << "p1和p2不相等" << endl;
}
if (p1 != p2)
{
cout << "p1和p2不相等" << endl;
}
else
{
cout << "p1和p2相等" << endl;
}
}
int main()
{
test01();
return 0;
}
6.函数调用运算符
函数调用运算符{}也可以重载
由于重载后使用的方式非常像函数的调用,因此称为仿函数
仿函数没有固定写法,非常灵活
//函数调用运算符重载
class MyPrint
{
public:
void operator()(string text)
{
cout << text << endl;
}
};
void test01()
{
MyPrint myPrint;
myPrint("hello world");
}
class MyAdd
{
public:
MyAdd()
{
this->count = 0;
}
int operator()(int a, int b)
{
this->count++;
return a + b;
}
int count;
};
int main()
{
test01();
MyAdd myAdd;
cout << myAdd(1, 2) << endl;
cout << myAdd.count << endl;
return 0;
}
二.继承
继承时面向对象三大特性之一
1.继承的基本语法
继承的基本语法
class 子类:继承方式 父类
{
子类新增的成员
父类的成员
};
2. 继承方式
//公共继承、私有继承、保护继承
//公共继承:public
//私有继承:private
//保护继承:protected
class Base1
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1 :public Base1
{
public:
void func()
{
m_A = 10; //父类中的公共权限成员,到子类中依然是公共权限
m_B = 10; //父类中的保护权限成员,到子类中依然是保护权限
//m_C = 10; //父类中的私有权限成员,到子类中访问不到
}
};
void test01()
{
Son1 s1;
s1.m_A = 100; //父类中的公共权限成员,到子类中依然是公共权限
//s1.m_B = 100; //父类中的保护权限成员,到子类中访问不到
//s1.m_C = 100; //父类中的私有权限成员,到子类中访问不到
}
3.继承中的对象模型
//继承中的对象模型
class Base1
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1 : public Base1
{
public:
int m_D;
};
void test01()
{
//父类中所有非静态成员属性都会被子类继承下去
//父类中私有成员属性是被编译器隐藏了,因此是访问不到,但是确实被继承下去了
cout << "sizeof Son1 = " << sizeof(Son1) << endl;
}
int main()
{
test01();
return 0;
}
输出结果
sizeof Son1 = 16
从输出中可以看出有四个int也就是父类的属性都被子类继承了,但是正如代码中所说,父类的私有成员属性被编译器隐藏了,因此是访问不到的。
4.构造和析构顺序
//继承中构造和析构的顺序
class Base
{
public:
Base()
{
cout << "Base的构造函数" << endl;
}
~Base()
{
cout << "Base的析构函数" << endl;
}
};
class Son : public Base
{
public:
Son()
{
cout << "Son的构造函数" << endl;
}
~Son()
{
cout << "Son的析构函数" << endl;
}
};
void test01()
{
Son s1;
}
int main()
{
test01();
return 0;
}
Base的构造函数
Son的构造函数
Son的析构函数
Base的析构函数
从代码中看出构造和析构顺序为:父类构造 >> 子类构造 >> 子类析构 >> 父类析构
5.同名成员处理
- 访问子类同名成员,直接访问即可
- 访问父类同名成员,需要加作用域
//继承中同名成员处理
//1.子类会隐藏父类中同名成员函数,需要加作用域
//2.子类中出现和父类同名的成员函数,会隐藏父类中所有同名成员函数
//3.如果想访问父类中被隐藏的同名成员函数,需要加作用域
//4.通过using可以将父类中所有同名成员函数引入到子类中
class Base
{
public:
void func()
{
cout << "Base func调用" << endl;
}
void func(int a)
{
cout << "Base func(int a)调用" << endl;
}
};
class Son : public Base
{
public:
void func()
{
cout << "Son func调用" << endl;
}
};
void test01()
{
Son s;
s.func();
s.Base::func();
}
6.多继承语法
//多继承语法
class Base1
{
public:
Base1()
{
m_A = 100;
}
int m_A;
};
class Base2
{
public:
Base2()
{
m_A = 200;
}
int m_A;
};
class Son : public Base1, public Base2
{
public:
Son()
{
m_C = 300;
m_D = 400;
}
int m_C;
int m_D;
};
void test01()
{
Son s;
cout << "sizeof Son = " << sizeof(s) << endl;
cout << "Base1::m_A = " << s.Base1::m_A << endl;
cout << "Base2::m_A = " << s.Base2::m_A << endl;
}
int main()
{
test01();
return 0;
}
7.菱形继承(拓展)
- 在多继承中,如果一个类从多个基类继承,而这些基类又有共同的基类,就会形成所谓的菱形继承结构。在这种情况下,最底层的派生类会继承多个来自顶层基类的成员,这会导致冗余和歧义。
- 虚继承可以解决这个问题。通过虚继承,多个中间层的类可以共享同一个顶层基类的成员,而不是各自拥有一份。这样,最底层的派生类就只会继承一份顶层基类的成员。
//菱形继承
class Animal
{
public:
int m_Age;
};
//利用虚继承 解决菱形继承问题
//继承之前加上关键字virtual变为虚继承
//Animal类称为虚基类
class Sheep : virtual public Animal
{
};
class Tuo : virtual public Animal
{
};
class SheepTuo : public Sheep, public Tuo
{
};
void test01()
{
SheepTuo st;
st.Sheep::m_Age = 18;
st.Tuo::m_Age = 28;
cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl;
cout << "st.m_Age = " << st.m_Age << endl;
}
int main()
{
test01();
return 0;
}
总结
本文介绍了运算符重载及继承的相关概念及代码呈现,希望能帮助到大家,如有不足,还望指出。