
c++c++使用类(运算符重载)
运算符重载
运算符重载基础
1)返回自定义数据类型的引用可以让多个运算符表达式串联起来。(不要返回局部变量的引用)
2)重载函数参数列表中的顺序决定了操作数的位置。
3)重载函数的参数列表中至少有一个是用户自定义的类型,防止程序员为内置数据类型重载运算符。
4)如果运算符重载既可以是成员函数也可以是全局函数,应该优先考虑成员函数,这样更符合运算符重载的初衷。
5)重载函数不能违背运算符原来的含义和优先级。
6)不能创建新的运算符。
重载加减运算符
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl // 超女类CGirl。
{
friend CGirl &operator+(CGirl& g, int score);
friend CGirl& operator+(int score, CGirl& g);
friend CGirl& operator+(CGirl& g1, CGirl& g2);
//重载减号
friend CGirl &operator-(CGirl& g, int score);
friend CGirl& operator-(int score, CGirl& g);
friend CGirl& operator-(CGirl& g1, CGirl& g2);
private:
int m_xw; // 胸围。
int m_score; // 分数。
public:
string m_name; // 姓名。
// 默认构造函数。
CGirl() { m_name = "西施"; m_xw = 87; m_score = 30; }
// 自我介绍的方法。
void show() { cout << "姓名:" << m_name << ",胸围:" << m_xw << ",评分:" << m_score << endl; }
//CGirl& operator-(int score) // 给超女减分的函数。
//{
// m_score = m_score - score;
// return *this;
//}
};
//重载加号运算符
CGirl& operator+(CGirl& g, int score) // 给超女加分的函数。
{
g.m_score = g.m_score + score;
return g;
}
CGirl& operator+(int score,CGirl& g) // 给超女加分的函数。
{
g.m_score = g.m_score + score;
return g;
}
CGirl& operator+(CGirl& g1, CGirl& g2) // 给超女加分的函数。
{
g1.m_score = g1.m_score + g2.m_score;
return g1;
}
//重载减号运算符
CGirl& operator-(CGirl& g, int score) // 给超女加分的函数。
{
g.m_score = g.m_score - score;
return g;
}
CGirl& operator-(int score,CGirl& g) // 给超女加分的函数。
{
g.m_score = g.m_score - score;
return g;
}
CGirl& operator-(CGirl& g1, CGirl& g2) // 给超女加分的函数。
{
g1.m_score = g1.m_score - g2.m_score;
return g1;
}
int main()
{
// 导演的要求:每轮表演之后,给超女加上她的得分。
CGirl g;
cout<<"重载加号运算符"<<endl;
operator+(g,30);
g.show();
g+30;
g.show();
g = g+g;
g.show();
cout<<"重载减号运算符"<<endl;
operator-(g,30);
g.show();
g-30;
g.show();
g = g-g;
g.show();
}
运算符重载函数的返回值类型要与运算符本身的含义一致。
成员函数版本的重载运算符函数:形参个数比运算符的操作数个数少一个,其中的一个操作数隐式传递了调用对象。
如果同时重载了非成员函数和成员函数版本,会出现二义性。
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl // 超女类CGirl。
{
// friend CGirl &operator+(CGirl& g, int score);
// friend CGirl& operator+(int score, CGirl& g);
// friend CGirl& operator+(CGirl& g1, CGirl& g2);
// //重载减号
// friend CGirl &operator-(CGirl& g, int score);
// friend CGirl& operator-(int score, CGirl& g);
// friend CGirl& operator-(CGirl& g1, CGirl& g2);
private:
int m_xw; // 胸围。
int m_score; // 分数。
public:
string m_name; // 姓名。
// 默认构造函数。
CGirl() { m_name = "西施"; m_xw = 87; m_score = 30; }
// 自我介绍的方法。
void show() { cout << "姓名:" << m_name << ",胸围:" << m_xw << ",评分:" << m_score << endl; }
//成员函数的重载
CGirl& operator-(int score) // 给超女减分的函数。
{
m_score = m_score - score;
return *this;
}
};
// //非成员函数的重载 重载加号运算符
// CGirl& operator+(CGirl& g, int score) // 给超女加分的函数。
// {
// g.m_score = g.m_score + score;
// return g;
// }
// CGirl& operator+(int score,CGirl& g) // 给超女加分的函数。
// {
// g.m_score = g.m_score + score;
// return g;
// }
// CGirl& operator+(CGirl& g1, CGirl& g2) // 给超女加分的函数。
// {
// g1.m_score = g1.m_score + g2.m_score;
// return g1;
// }
// //重载减号运算符
// CGirl& operator-(CGirl& g, int score) // 给超女加分的函数。
// {
// g.m_score = g.m_score - score;
// return g;
// }
// CGirl& operator-(int score,CGirl& g) // 给超女加分的函数。
// {
// g.m_score = g.m_score - score;
// return g;
// }
// CGirl& operator-(CGirl& g1, CGirl& g2) // 给超女加分的函数。
// {
// g1.m_score = g1.m_score - g2.m_score;
// return g1;
// }
int main()
{
// 导演的要求:每轮表演之后,给超女加上她的得分。
CGirl g;
// cout<<"重载加号运算符"<<endl;
// operator+(g,30);
// g.show();
// g+30;
// g.show();
// g = g+g;
// g.show();
cout<<"重载减号运算符"<<endl;
// operator-(g,30);
// g.show();
g-30-5;
g.show();
// g = g-g;
// g.show();
}
以下运算符只能通过成员函数进行重载:
- = 赋值运算符
- () 函数调用运算符
- [] 下标运算符
- -> 通过指针访问类成员的运算符
重载关系运算符
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl // 超女类CGirl。
{
string m_name; // 姓名。
int m_yz; // 颜值:1-千年美人;2-百年美人;3-绝代美人;4-极漂亮;5-漂亮;6-一般;7-歪瓜裂枣。
int m_sc; // 身材:1-火辣;2-...;3-...;4-...;5-...;6-...;7-膘肥体壮。
int m_acting; // 演技:1-完美;2-...;3-...;4-...;5-...;6-...;7-四不像。
public:
// 四个参数的构造函数。
CGirl(string name, int yz, int sc, int acting) { m_name = name; m_yz = yz; m_sc = sc; m_acting = acting; }
// 比较两个超女的商业价值。
bool operator==(const CGirl& g1) // 相等==
{
if ((m_yz + m_sc + m_acting) == (g1.m_yz + g1.m_sc + g1.m_acting)) return true;
return false;
}
bool operator>(const CGirl& g1) // 大于>
{
if ((m_yz + m_sc + m_acting) < (g1.m_yz + g1.m_sc + g1.m_acting)) return true;
return false;
}
bool operator<(const CGirl& g1) // 小于<
{
if ((m_yz + m_sc + m_acting) > (g1.m_yz + g1.m_sc + g1.m_acting)) return true;
return false;
}
};
int main()
{
CGirl g1("西施", 1, 2, 12), g2("冰冰", 1, 2, 2);
if (g1==g2)
cout << "西施和冰冰的商业价值相同。\n";
else
if (g1>g2)
cout << "西施商业价值相同比冰冰大。\n";
else
cout << "冰冰商业价值相同比西施大。\n";
}
重载左移运算符
重载左移运算符(<<)用于输出自定义对象的成员变量
只能使用非成员函数版本。
如果要输出对象的私有成员,可以配合友元一起使用
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl // 超女类CGirl。
{
friend ostream& operator<<(ostream& cout, const CGirl& g);
string m_name; // 姓名。
int m_xw; // 胸围。
int m_score; // 评分。
public:
// 默认构造函数。
CGirl() { m_name = "西施"; m_xw = 87; m_score = 30; }
// 自我介绍的方法。
void show() { cout << "姓名:" << m_name << ",胸围:" << m_xw << ",评分:" << m_score << endl; }
};
ostream& operator<<(ostream& cout, const CGirl& g)
{
cout << "姓名:" << g.m_name << ",胸围:" << g.m_xw << ",评分:" << g.m_score;
return cout;
}
int main()
{
CGirl g;
cout << g << endl;
}
成员函数的写法。。。(输出数据的话,不符合正常的使用逻辑)
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl // 超女类CGirl。
{
string m_name; // 姓名。
int m_xw; // 胸围。
int m_score; // 评分。
public:
// 默认构造函数。
CGirl() { m_name = "西施"; m_xw = 87; m_score = 30; }
// 自我介绍的方法。
void show() { cout << "姓名:" << m_name << ",胸围:" << m_xw << ",评分:" << m_score << endl; }
ostream& operator<<(ostream& cout)
{
cout << "姓名:" << this->m_name << ",胸围:" << this->m_xw << ",评分:" << this->m_score;
return cout;
}
};
int main()
{
CGirl g;
g<<cout << endl;
}
重载下标运算符
返回值类型 &perator[](参数);
const 返回值类型 &operator[](参数) const;
使用第一种声明方式,[]不仅可以访问数组元素,还可以修改数组元素。
使用第二种声明方式,[]只能访问而不能修改数组元素。
在实际开发中,我们应该同时提供以上两种形式,这样做是为了适应const对象,因为通过const 对象只能调用const成员函数,如果不提供第二种形式,那么将无法访问const对象的任何数组元素。
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl // 超女类CGirl。
{
private:
string m_boys[3]; // 超女的男朋友
public:
string m_name; // 姓名。
// 默认构造函数。
CGirl() { m_boys[0] = "子都"; m_boys[1] = "潘安"; m_boys[2] = "宋玉"; }
// 显示全部男朋友的姓名。
void show() { cout << m_boys[0] << "、" << m_boys[1] << "、" << m_boys[2] << endl; }
string& operator[](int ii)
{
return m_boys[ii];
}
const string& operator[](int ii) const
{
return m_boys[ii];
}
};
int main()
{
CGirl g; // 创建超女对象。
g[1] = "王麻子";
cout << "第1任男朋友:" << g[1] << endl;
g.show();
cout<<"常对象只能访问常函数。。"<<endl;
const CGirl g1 = g;
cout << "第1任男朋友:" << g1[1] << endl;
}
重载赋值运算符
默认赋值函数, 对成员变量进行浅拷贝。
对象的赋值运算是用一个已经存在的对象,给另一个已经存在的对象赋值。
如果类的定义中没有重载赋值函数,编译器就会提供一个默认赋值函数。
如果类中重载了赋值函数,编译器将不提供默认赋值函数。
语法:
类名 & operator=(const 类名 & 源对象);
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl // 超女类CGirl。
{
public:
int m_bh; // 编号。
string m_name; // 姓名。
int* m_ptr; // 计划使用堆区内存。
CGirl() { m_ptr = nullptr; }
~CGirl() { if (m_ptr) delete m_ptr; }
// 显示全部成员变量。
void show() { cout << "编号:" << m_bh << ",姓名:" << m_name << ",m_ptr=" << m_ptr <</* ",*m_ptr=" << *m_ptr<< */endl; }
//重载等号运算符
CGirl& operator=(const CGirl& g)
{
if (this == &g) return *this; // 如果是自己给自己赋值。(this指针的解引用)
if (g.m_ptr == nullptr) // 如果源对象的指针为空,则清空目标对象的内存和指针。
{
if (m_ptr != nullptr) { delete m_ptr; m_ptr = nullptr; }
}
else // 如果源对象的指针不为空。
{
// 如果目标对象的指针为空,先分配内存。
if (m_ptr == nullptr) m_ptr = new int;
// 然后,把源对象内存中的数据复制到目标对象的内存中。
memcpy(m_ptr, g.m_ptr, sizeof(int));
}
m_bh = g.m_bh; m_name = g.m_name;
cout << "调用了重载赋值函数。\n" << endl;
return *this;
}
};
int main()
{
CGirl g1, g2; // 创建超女对象。
g1.m_bh = 8; g1.m_name = "西施"; g1.m_ptr = new int(3);
g1.show();
g2.show();
g2 = g1;
g2.show();
cout << "*g1.m_ptr=" << *g1.m_ptr << ",*g2.m_ptr=" << *g2.m_ptr << endl;
}
重载new&delete运算符
在C++中,使用new时,编译器做了两件事情:
1)调用标准库函数operator new()分配内存; (返回的是申请内存空间的起始地址)
2)调用构造函数初始化内存; (把内存中的值初始化)
使用delete时,也做了两件事情:
1)调用析构函数;
2)调用标准库函数operator delete()释放内存。
为一个类重载new和delete时,尽管不必显式地使用static,但实际上仍在创建 static 成员函数。
为整形变量动态分配内存
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
void* operator new(size_t size) // 参数必须是size_t(unsigned long long),返回值必须是void*。
{
void* ptr = malloc(size); // 申请内存。
return ptr;
}
void operator delete(void* ptr) // 参数必须是void *,返回值必须是void。
{
if (ptr == 0) return; // 对空指针delete是安全的。
free(ptr); // 释放内存。
}
int main()
{
int* p1 = new int(3);
//int *a=(int *)malloc(sizeof(int)*10);
cout<<"p1="<<p1<<" (void*)p1="<<(void*)p1<<" *p1="<<*p1<<endl;
delete p1;
}
为类动态分配内存
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
void* operator new(size_t size) // 参数必须是size_t(unsigned long long),返回值必须是void*。
{
void* ptr = malloc(size); // 申请内存。
return ptr;
}
void operator delete(void* ptr) // 参数必须是void *,返回值必须是void。
{
if (ptr == 0) return; // 对空指针delete是安全的。
free(ptr); // 释放内存。
}
class CGirl
{
public:
int m_bh;
int m_xw;
CGirl(int bh,int xw){
m_bh = bh;
m_xw = xw;
cout<<"调用构造函数"<<endl;
}
~CGirl(){
cout<<"调用析构函数"<<endl;
}
};
int main()
{
// int* p1 = new int(3);
// //int *a=(int *)malloc(sizeof(int)*10);
// cout<<"p1="<<p1<<" (void*)p1="<<(void*)p1<<" *p1="<<*p1<<endl;
// delete p1;
CGirl* p2 =new CGirl(2,2);
delete p2;
}
先调用重载的new函数申请内存,然后调用构造函数
先调用析构函数,再调用重载的delete函数;
重载的new和delete可以是全局函数,也可以是类的成员函数。
为一个类重载new和delete时,尽管不必显式地使用static,但实际上仍在创建static成员函数。
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl
{
public:
int m_bh;
int m_xw;
CGirl(int bh,int xw){
m_bh = bh;
m_xw = xw;
cout<<"调用构造函数"<<endl;
}
~CGirl(){
cout<<"调用析构函数"<<endl;
}
//是静态方法
void* operator new(size_t size) // 参数必须是size_t(unsigned long long),返回值必须是void*。
{
void* ptr = malloc(size); // 申请内存。
return ptr;
}
void operator delete(void* ptr) // 参数必须是void *,返回值必须是void。
{
if (ptr == 0) return; // 对空指针delete是安全的。
free(ptr); // 释放内存。
}
};
int main()
{
cout<<"使用的是c++的new和delete"<<endl;
int* p1 = new int(3);
//int *a=(int *)malloc(sizeof(int)*10);
cout<<"p1="<<p1<<" (void*)p1="<<(void*)p1<<" *p1="<<*p1<<endl;
delete p1;
cout<<"使用的是自定义的new和delete(static成员函数)"<<endl;
CGirl* p2 =new CGirl(2,2);
delete p2;
}
内存池
重载new和delete函数主要就是为了实现内存池。。
内存池
- 预先分配一大块内存空间
- 提升分配和归还速度
- 减少内存碎片
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl // 超女类CGirl。
{
public:
int m_bh; // 编号。
int m_xw; // 胸围。
static char* m_pool; // 内存池的起始地址。
static bool initpool() // 个初始化内存池的函数。
{
m_pool = (char*)malloc(18); // 向系统申请18字节的内存。
if (m_pool == 0) return false; // 如果申请内存失败,返回false。
memset(m_pool, 0, 18); // 把内存池中的内容初始化为0。
cout << "内存池的起始地址是:" << (void*)m_pool << endl;
return true;
}
static void freepool() // 释放内存池。
{
if (m_pool == 0) return; // 如果内存池为空,不需要释放,直接返回。
free(m_pool); // 把内存池归还给系统。
cout << "内存池已释放。\n";
}
CGirl(int bh, int xw) { m_bh = bh, m_xw = xw; cout << "调用了构造函数CGirl()\n"; }
~CGirl() { cout << "调用了析构函数~CGirl()\n"; }
void* operator new(size_t size) // 参数必须是size_t(unsigned long long),返回值必须是void*。
{
if (m_pool[0] == 0) // 判断第一个位置是否空闲。
{
cout << "分配了第一块内存:" << (void*)(m_pool + 1) << endl;
m_pool[0] = 1; // 把第一个位置标记为已分配。
return m_pool + 1; // 返回第一个用于存放对象的址。
}
if (m_pool[9] == 0) // 判断第二个位置是否空闲。
{
cout << "分配了第二块内存:" << (void*)(m_pool + 9) << endl;
m_pool[9] = 1; // 把第二个位置标记为已分配。
return m_pool + 9; // 返回第二个用于存放对象的址。
}
// 如果以上两个位置都不可用,那就直接系统申请内存。
void* ptr = malloc(size); // 申请内存。
cout << "申请到的内存的地址是:" << ptr << endl;
return ptr;
}
void operator delete(void* ptr) // 参数必须是void *,返回值必须是void。
{
if (ptr == 0) return; // 如果传进来的地址为空,直接返回。
if (ptr == m_pool + 1) // 如果传进来的地址是内存池的第一个位置。
{
cout << "释放了第一块内存。\n";
m_pool[0] = 0; // 把第一个位置标记为空闲。
return;
}
if (ptr == m_pool + 9) // 如果传进来的地址是内存池的第二个位置。
{
cout << "释放了第二块内存。\n";
m_pool[9] = 0; // 把第二个位置标记为空闲。
return;
}
// 如果传进来的地址不属于内存池,把它归还给系统。
free(ptr); // 释放内存。
}
};
char* CGirl::m_pool = 0; // 初始化内存池的指针。
int main()
{
// 初始化内存池。
if (CGirl::initpool()==false) { cout << "初始化内存池失败。\n"; return -1; }
CGirl* p1 = new CGirl(3, 8); // 将使用内存池的第一个位置。
cout << "p1的地址是:" << p1 << ",编号:" << p1->m_bh << ",胸围:" << p1->m_xw << endl;
CGirl* p2 = new CGirl(4, 7); // 将使用内存池的第二个位置。
cout << "p2的地址是:" << p2 << ",编号:" << p2->m_bh << ",胸围:" << p2->m_xw << endl;
CGirl* p3 = new CGirl(6, 9); // 将使用系统的内存。
cout << "p3的地址是:" << p3 << ",编号:" << p3->m_bh << ",胸围:" << p3->m_xw << endl;
delete p1; // 将释放内存池的第一个位置。
CGirl* p4 = new CGirl(5, 3); // 将使用内存池的第一个位置。
cout << "p4的地址是:" << p4 << ",编号:" << p4->m_bh << ",胸围:" << p4->m_xw << endl;
delete p2; // 将释放内存池的第二个位置。
delete p3; // 将释放系统的内存。
delete p4; // 将释放内存池的第一个位置。
CGirl::freepool(); // 释放内存池。
}
重载括号运算符
括号运算符重载函数的语法:
返回值类型 operator()(参数列表);
括号运算符必须以成员函数的形式进行重载。
括号运算符重载函数具备普通函数全部的特征。
如果函数对象与全局函数同名,按作用域规则选择调用的函数。
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
void show(string str) // 向超女表白的函数。
{
cout << "普通函数:" << str << endl;
}
class CGirl // 超女类。
{
public:
void operator()(string str) // 向超女表白的函数。
{
cout << "重载函数:" << str << endl;
}
};
int main()
{
CGirl show;
cout<<"::调用全局函数"<<endl;
::show("我是一只傻傻鸟。");
cout<<"括号运算符重载(调用成员函数):"<<endl;
show("我是一只傻傻鸟。");
}
重载一元运算符
可重载的一元运算符。
1)++ 自增 2)-- 自减 3)! 逻辑非 4)& 取地址
5)~ 二进制反码 6)* 解引用 7)+ 一元加 8) - 一元求反
重载自增运算符的成员函数版本
C++ 规定,重载++或–时,如果重载函数有一个int形参,编译器处理后置表达式时将调用这个重载函数。
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl // 超女类CGirl。
{
public:
string m_name; // 姓名。
int m_ranking; // 排名。
// 默认构造函数。
CGirl() { m_name = "西施"; m_ranking = 5; }
// 自我介绍的方法。
void show() const { cout << "姓名:" << m_name << ",排名:" << m_ranking << endl; }
CGirl & operator++() // ++前置的重载函数。
{
m_ranking++; return *this;
}
CGirl operator++(int) // ++后置的重载函数。
{
CGirl tmp = *this;
m_ranking++;
return tmp; //返回临时对象
}
};
int main()
{
CGirl g1,g2; // 创建超女对象。
int ii=5 , jj=5;
int xx = ++(++(++ii)); cout << "xx=" << xx << ",ii=" << ii << endl;
int yy = jj++; cout << "yy=" << yy << ",jj=" << jj << endl;
CGirl g3 = ++(++(++g1)); cout << "g3.m_ranking=" << g3.m_ranking << ",g1.m_ranking=" << g1.m_ranking << endl;
CGirl g4 = g2++; cout << "g4.m_ranking=" << g4.m_ranking << ",g2.m_ranking=" << g2.m_ranking << endl;
// g2.show();
}
更多推荐
所有评论(0)