运算符重载
关系运算符的重载
对于自定义数据类型,编译器不知道如何进行比较
重载 == !=号
bool operator==( Person & p)
bool operator!=(Person & p)
示例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
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;
}
else
{
return false;
}
}
bool operator!=(Person& p)
{
return !(this->m_Name == p.m_Name && this->m_Age == p.m_Age);
}
string m_Name;
int m_Age;
};
void test01()
{
int a = 10;
int b = 20;
int c = 30;
if (a == b)
{
cout << "a == b" << endl;
}
else
{
cout << "a != b" << endl;
}
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();
system("pause");
return EXIT_SUCCESS;
}
函数调用运算符重载
重载 ()
使用时候很像函数调用,因此称为仿函数
void operator()(*string* text)
int operator()(int a,int b)
仿函数写法不固定,比较灵活
*cout* << MyAdd()(1, 1) << *endl*; // 匿名函数对象 特点:当前行执行完立即释放
示例:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>
class MyPrint
{
public:
void operator()(string text)
{
cout << text << endl;
}
};
void myPrint2(string text)
{
cout << text << endl;
}
class MyAdd
{
public:
int operator()(int a, int b)
{
return a + b;
}
};
void test01()
{
MyPrint myprint;
myprint("hello world"); //仿函数,本质是一个对象,重载了一下小括号,函数对象
myPrint2("hello world"); //普通函数
}
void test02()
{
MyAdd myadd;
cout << myadd(1, 1) << endl;
cout << MyAdd()(1, 2) << endl;//MyAdd()匿名对象,当前行执行完立即释放,是使用仿函数的一种快速方式
}
int main()
{
test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
不要重载 && 和 ||
原因是无法实现短路特性
建议:将<< 和 >>写成全局函数,其他可重载的符号写到成员即可
强化训练-字符串类封装
myString类 实现自定义的字符串类
属性
char * pString; 维护 在堆区真实开辟的字符数组
int m_Size; 字符串长度
行为
有参构造 MyString(char * str)
拷贝构造 MyString(const MyString & str);
析构 ~MyString();
重载<< 运算符
重载 >> 运算符
重载 = 赋值运算
重载 [] str[0] 按照索引位置设置获取字符
重载 + 字符串拼接
重载 == 对比字符串
示例
头文件
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class MyString
{
public:
friend ostream& operator<<(ostream& cout, MyString& str);
friend istream& operator>>(istream& cin, MyString& str);
//有参构造
MyString(const char* str);
//拷贝构造
MyString(const MyString& str);
//重载 = 运算符
MyString& operator=(const char * str);
MyString& operator=(const MyString& str);
//重载[]运算符
char& operator[](int index);
//重载+运算符
MyString operator+(const char* str);
MyString operator+(const MyString& str);
//重载==运算符
bool operator==(const char * str);
bool operator==(const MyString& str);
~MyString();
private:
char* pString;//维护在堆区开辟的字符数组
int m_Size;//字符串长度 不统计\0
};
实现
#include "MyString.h"
//重载左移运算符
ostream& operator<<(ostream& cout, MyString& str)
{
cout << str.pString << endl;
return cout;
}
//重载右移运算符
istream& operator>>(istream& cin, MyString& str)
{
//先清空原来堆区数据
if (str.pString)
{
delete[] str.pString;
str.pString = NULL;
}
char buf[1024];//开辟临时数组 记录用户输入内容
cin >> buf;
str.pString = new char[strlen(buf) + 1];
strcpy(str.pString, buf);
str.m_Size = strlen(buf);
return cin;
}
MyString::MyString(const char* str)
{
cout << "MyString 有参构造函数调用" << endl;
this->pString = new char[strlen(str) + 1];
strcpy(this->pString, str);
this->m_Size = strlen(str);
}
MyString::MyString(const MyString& str)
{
cout << "MyString 拷贝构造函数调用" << endl;
this->pString = new char[strlen(str.pString)+1 ];
strcpy(this->pString, str.pString);
this->m_Size = str.m_Size;
}
MyString& MyString::operator= (const char* str)
{
//先判断原来堆区是否有内容,如果有先释放
if (this->pString != NULL)
{
delete[]this->pString;
this->pString = NULL;
}
this->pString = new char[strlen(str) + 1];
strcpy(this->pString, str);
this->m_Size = strlen(str);
return *this;
}
MyString& MyString::operator= (const MyString& str)
{
//先判断原来堆区是否有内容,如果有先释放
if (this->pString != NULL)
{
delete[]this->pString;
this->pString = NULL;
}
this->pString = new char[strlen(str.pString) + 1];
strcpy(this->pString, str.pString);
this->m_Size = strlen(str.pString);
return *this;
}
char& MyString::operator[](int index)
{
return this->pString[index];
}
MyString MyString::operator+(const char* str)
{
//本身 abc 传入def
// 计算开辟内存大小
int newSize = this->m_Size + strlen(str) + 1;
char * temp=new char[newSize];
memset(temp, 0, newSize);
strcat(temp, this->pString);
strcat(temp, str);
MyString newString = temp;//调用我们写好的有参构造来初始化newString
delete[] temp;//把临时指针释放掉
return newString;
}
MyString MyString::operator+(const MyString& str)
{
int newSize = this->m_Size + strlen(str.pString) + 1;
char* temp = new char[newSize];
memset(temp, 0, newSize);
strcat(temp, this->pString);
strcat(temp, str.pString);
MyString newString = temp;//调用我们写好的有参构造来初始化newString
delete[] temp;//把临时指针释放掉
return newString;
}
bool MyString::operator==(const char* str)
{
if (strcmp(this->pString, str) == 0)
{
return true;
};
return false;
}
bool MyString::operator==(const MyString& str)
{
if (strcmp(this->pString, str.pString) == 0)
{
return true;
};
return false;
}
MyString::~MyString()
{
cout << "MyString 析构函数调用" << endl;
if (this->pString != NULL)
{
delete[]this->pString;
this->pString = NULL;
}
}
主函数
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include "MyString.h"
void test01()
{
MyString str = "abc";
//MyString str2 = str;
cout << str << endl;
cout << "请重新给str赋值" << endl;
cin >> str;
cout << "str新的值为:" << str << endl;
MyString str2 = str;
cout << "str2 = " << str2 << endl;
}
void test02()
{
MyString str = "abcd";
MyString str2 = " ";
str2 = str;
cout << "str2= " << str2 << endl;
cout << str2[0] << endl;
str2[0] = 'z';
cout << "str2[0]改为z后输出" << str2[0] << endl;
MyString str3 = "abc";
MyString str4 = "def";
MyString str5 = str3 + str4;
cout << "str5" << str5 << endl;
MyString str6 = str5 + "ghe";
cout << "str6" << str6 << endl;
if (str5 == str6)
{
cout << "str5 == str6" << endl;
}
else
{
cout << "str5 != str6" << endl;
}
if (str6 == "abcdefghe")
{
cout << "str6 == abcdefghe" << endl;
}
else
{
cout << "str6 != abcdefghe" << endl;
}
}
int main()
{
//test01();
//int a = 10;
//cin >> a;
//cout << "a = " << a << endl;
test02();
system("pause");
return EXIT_SUCCESS;
}
继承
继承基本语法
继承优点:减少重复的代码,提高代码复用性
语法: class 子类 : 继承方式 父类
// News 子类 派生类
// BasePage 父类 基类
示例
//利用继承模拟网页
//集成优点:减少重复的代码,提高代码复用性
class BasePage
{
public:
void header()
{
cout << "公共的头部" << endl;
}
void footer()
{
cout << "公共的底部" << endl;
}
void leftList()
{
cout << "公共的左侧列表" << endl;
}
};
// class 子类 : 继承方式 父类
// News 子类 也叫 派生类
// BasePage 父类 也叫 基类
class News : public BasePage
{
public:
void content()
{
cout << "新闻播报" << endl;
}
};
class Sport :public BasePage
{
public:
void content()
{
cout << "世界杯" << endl;
}
};
void test01()
{
News news;
cout << "新闻界面的内容如下:" << endl;
news.header();
news.footer();
news.leftList();
news.content();
Sport sp;
cout << "体育界面的内容如下:" << endl;
sp.header();
sp.footer();
sp.leftList();
sp.content();
}
int main()
{
test01();
system("pause");
return EXIT_SUCCESS;
}
继承方式
公共继承
父类中公共权限,子类中变为公共权限
父类中保护权限,子类中变为保护权限
父类中私有权限,子类访问不到
保护继承
父类中公共权限,子类中变为保护权限
父类中保护权限,子类中变为保护权限
父类中私有权限,子类访问不到
私有继承
父类中公共权限,子类中变为私有权限
父类中保护权限,子类中变为私有权限
父类中私有权限,子类访问不到
示例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
// 公共继承
class Base1
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1 : public Base1
{
public:
void func()
{
m_A = 100; //父类中公共权限,子类中还是公共权限
m_B = 100; //父类中的保护权限,子类中还是保护权限
//m_C = 100; //父类中私有成员,子类无法访问
}
};
// 保护继承
class Base2
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son2 : protected Base2
{
public:
void func()
{
m_A = 100; //父类中公共权限,子类中变为保护权限
m_B = 100; //父类中的保护权限,子类中还是保护权限
//m_C = 100; //报错 父类中私有成员,子类无法访问
}
};
// 私有继承
class Base3
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son3 : private Base3
{
public:
void func()
{
m_A = 100; //父类中公共权限,子类中变为私有权限
m_B = 100; //父类中的保护权限,子类中还是私有权限
//m_C = 100; //报错 父类中私有成员,子类无法访问
}
};
class GrandSon3 : public Son3
{
public:
void func()
{
m_A = 100; //Son3中是私有权限,GrandSon3访问不到
m_B = 100; //Son3中是私有权限,GrandSon3访问不到
}
};
void test01()
{
Son1 s1;
s1.m_A = 100;//在Son1 中 m_A 是公共权限 类外可以访问
//s1.m_B = 100;//报错,Son1 中 m_B是保护权限 类外不能访问
Son2 s2;
//s2.m_A = 100;//报错,在Son2 中 m_A 是保护权限 类外不能访问
//s2.m_B = 100;//报错,在Son1 中 m_B 是保护权限 类外不能访问
Son3 s3;
s3.m_A = 100; //报错,在Son3中是私有权限,类外访问不到
s3.m_A = 100; //报错,在Son3中是私有权限,类外访问不到
}
int main()
{
system("pause");
return EXIT_SUCCESS;
}
继承中的对象模型
父类中的私有属性,子类是继承下去了,只不过由编译器给隐藏了,访问不到
可以利用开发人员工具查看对象模型
C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts
打开开发人员命令工具
跳转盘符 E:
跳转文件路径 cd到文件路径下
cl /d1 reportSingleClassLayout类名 文件名
继承中的构造和析构
先调用父类构造,再调用其他成员构造, 再调用自身构造 ,析构的顺序与构造相反
利用初始化列表语法 显示调用父类中的其他构造函数
父类中 构造、析构、拷贝构造 、operator= 是不会被子类继承下去的
原因是只有父类知道如何初始化自己的属性
示例:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Base1
{
public:
Base1()
{
cout << "Base1的构造函数调用" << endl;
}
~Base1()
{
cout << "Base1的析构函数调用" << endl;
}
};
class Other
{
public:
Other()
{
cout << "Other的构造函数调用" << endl;
}
~Other()
{
cout << "Other的析构函数调用" << endl;
}
};
class Son1 :public Base1
{
public:
Son1()
{
cout << "Son1的构造函数调用" << endl;
}
~Son1()
{
cout << "Son1的析构函数调用" << endl;
}
Other other;
};
void test01()
{
Son1 s; //先调用父类构造,再调用自身构造,析构的顺序与构造相反
//当有其他类作为本类成员的时候,先调用父类构造,再调用其他类构造,在调用自身构造,析构的顺序与构造相反
}
class Base2
{
public:
Base2(int a)
{
this->m_A = a;
cout << "Base2的构造函数调用" << endl;
}
int m_A;
};
class Son2 :public Base2
{
public:
Son2(int a) :Base2(a) //利用初始化列表语法,显示调用父类中的其他构造函数
//也可以写成 Son2() :Base2(10)但就相当于写死了
//也可以给父类增加默认构造函数
{
cout << "Son2的构造函数调用" << endl;
}
};
void test02()
{
Son2 s2(100); //此处100是传给父类的有参构造的
}
//父类中的默认构造、析构、拷贝构造、operator=是不会被子类继承下去的
int main()
{
test01();
system("pause");
return EXIT_SUCCESS;
}
继承中的同名成员处理
我们可以利用作用域 访问父类中的同名成员
当子类重新定义了父类中的同名成员函数,子类的成员函数会 隐藏掉父类中所有重载版本的同名成员,可以利用作用域显示指定调用
示例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Base
{
public:
Base()
{
this->m_A = 10;
}
void func()
{
cout << "Base func调用" << endl;
}
void func(int a)
{
cout << "Base 有参func调用" << endl;
}
int m_A;
};
class Son : public Base
{
public:
Son()
{
this->m_A = 20;
}
void func()
{
cout << "Son func调用" << endl;
}
int m_A;
};
void test01()
{
Son s1;
cout << "s1.m_A = " << s1.m_A << endl; //子类中有跟父类同名成员,优先访问子类
//我们可以利用作用域,访问父类同名成员
cout << "Base 中的m_A = " << s1.Base::m_A << endl;
}
void test02()
{
Son s2;
s2.func();
s2.Base::func();
//s2.func(1); 报错
//原因:当子类重新定义了父类中的同名成员函数,子类的成员函数会隐藏掉父类中所有重载版本的同名成员
//可以利用作用域显示同名调用
s2.Base::func(1);
}
int main()
{
test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
继承中的同名 静态成员处理
结论和 非静态成员 一致
只不过调用方式有两种
通过对象
通过类名
通过类名的方式 访问 父类作用域下的m_A静态成员变量
Son::Base::m_A
多继承基本语法
class 子类 : 继承方式 父类1 , 继承方式 父类2
当多继承的两个父类中有同名成员,需要加作用域区分
示例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Base1
{
public :
Base1()
{
this->m_A = 10;
this->m_E = 30;
}
int m_A;
int m_E;
};
class Base2
{
public:
Base2()
{
this->m_B = 20;
this->m_E = 40;
}
int m_B;
int m_E;
};
//多继承
class Son : public Base1, public Base2
{
public:
int m_C;
int m_D;
};
void test01()
{
cout << "Size of Son = " << sizeof(Son) << endl;//16
Son s;
cout << s.m_A << endl;
cout << s.m_B << endl;
//当多继承的两个父类中有同名成员,需要加作用域区分
cout << s.Base1::m_E << endl;
cout << s.Base2::m_E << endl;
}
int main()
{
test01();
system("pause");
return EXIT_SUCCESS;
}
菱形继承
问题:
两个类有公共的父类 和共同的子类 ,发生菱形继承
菱形继承导致数据有两份,浪费资源
解决方案:利用虚继承可以解决菱形继承问题(左侧是虚继承内存结构,右边是未使用虚继承内存结构)
class Sheep : **virtual** public Animal{};
当发生虚继承后,sheep和tuo类中 继承了一个 vbptr指针 虚基类指针 指向的是一个 虚基类表 vbtable
虚基类表中记录了 偏移量 ,通过偏移量 可以找到唯一的一个m_Age
利用地址偏移找到 vbtable中的偏移量 并且访问数据
示例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//动物类
class Animal
{
public:
int m_Age;//年龄
};
//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 = 10;
st.Tuo::m_Age = 20;
cout << "Sheep::m_Age = " << st.Sheep::m_Age << endl;
cout << "Tuo::m_Age = " << st.Tuo::m_Age << endl;
cout << "age = " << st.m_Age << endl;
//均为20,虚继承情况下 m_Age只有一份
}
//虚继承内存结构,通过偏移量查看,test02不重要
void test02()
{
SheepTuo st;
st.m_Age = 10;
//通过Sheep找到 偏移量
//*(int *)&st 解引用到了 虚基类表中
cout << *((int *)*(int *)&st + 1) << endl;
//通过Tuo 找到偏移量
cout << *((int *)*((int *)&st + 1) + 1) << endl;
//通过偏移量 访问m_Age
cout << "m_Age = " << ((Animal *)((char *)&st + *((int *)*(int *)&st + 1)))->m_Age << endl;
cout << "m_Age = " << *((int *)((char *)&st + *((int *)*(int *)&st + 1))) << endl;
}
int main()
{
test01();
system("pause");
return EXIT_SUCCESS;
}