C++运算符重载的实现示例
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>1. 运算符重载的基本概念</li><li>2. 算术运算符重载</li><ul class="second_class_ul"><li>2.1 加法运算符重载</li><li>2.2 减法运算符重载</li></ul><li>3. 流运算符重载</li><ul class="second_class_ul"><li>3.1 输出运算符<<重载</li><li>3.2 避免不必要的拷贝</li></ul><li>4. 自增运算符重载</li><ul class="second_class_ul"><li>4.1 前置自增运算符</li><li>4.2 后置自增运算符</li></ul><li>5. 赋值运算符重载</li><ul class="second_class_ul"></ul><li>6. 关系运算符重载</li><ul class="second_class_ul"></ul><li>7. 函数调用运算符重载</li><ul class="second_class_ul"></ul><li>8. 运算符重载的规则与最佳实践</li><ul class="second_class_ul"><li>8.1 可重载的运算符</li><li>8.2 不可重载的运算符</li><li>8.3 最佳实践</li></ul><li>9. 总结</li><ul class="second_class_ul"></ul><li>完整代码(杂糅版选取复制):</li><ul class="second_class_ul"></ul></ul></div><p class="maodian"></p><h2>1. 运算符重载的基本概念</h2><p><span>运算符重载是C++一项强大的特性,它允许我们为自定义类型(类或结构体)重新定义运算符的行为。通过运算符重载,我们可以让自定义类型像内置类型一样使用标准的运算符语法,使代码更加直观和自然</span></p>
<p><strong>从本质上讲,运算符重载是函数重载的一种特殊形式。当我们重载一个运算符时,实际上是在定义一个特殊的成员函数或全局函数,函数名由<code>operator</code>关键字后接要重载的运算符组成</strong></p>
<div class="jb51code"><pre class="brush:cpp;">a + b; // 等价于 a.operator+(b) 或 operator+(a, b)
a == b; // 等价于 a.operator==(b) 或 operator==(a, b)</pre></div>
<p><strong>运算符重载提供了语法糖,让代码更加直观和易读。比较以下两种写法:</strong></p>
<div class="jb51code"><pre class="brush:cpp;">// 使用运算符重载
list1 + list2;
// 不使用运算符重载
list1.concat(list2);</pre></div>
<p><strong>第二种写法显然更加符合直觉,让自定义类型与内置类型有一致的操作方式</strong></p>
<p class="maodian"></p><h2>2. 算术运算符重载</h2>
<p class="maodian"></p><h3>2.1 加法运算符重载</h3>
<p><strong>在代码中,复数类的加法运算符重载展示了运算符重载的基本用法:</strong></p>
<div class="jb51code"><pre class="brush:cpp;">Complex operator+(Complex& a, Complex& b)
{
Complex ret;
ret.real = a.real + b.real;
ret.image = a.image + b.image;
return ret;
}</pre></div>
<p>运算符重载有两种实现方式:<strong>成员函数形式</strong>和<strong>全局函数形式</strong></p>
<p><span><strong>成员函数形式</strong>隐含一个<code>this</code>指针参数,只需要一个显式参数:</span></p>
<div class="jb51code"><pre class="brush:cpp;">class Complex {
public:
Complex operator+(const Complex& other) {
Complex ret;
ret.real = this->real + other.real;
ret.image = this->image + other.image;
return ret;
}
};</pre></div>
<p><strong>全局函数形式</strong>需要两个显式参数,通常需要声明为类的友元函数以访问私有成员</p>
<div class="jb51code"><pre class="brush:cpp;">class Complex {
friend Complex operator+(const Complex& a, const Complex& b);
// ...
};</pre></div>
<p><span>代码中使用了全局函数形式,并将它们声明为友元函数,这样可以访问<code>Complex</code>类的私有成员<code>real</code>和<code>image</code>。</span></p>
<p class="maodian"></p><h3>2.2 减法运算符重载</h3>
<p>减法运算符的重载与加法类似,只需改变运算逻辑:</p>
<div class="jb51code"><pre class="brush:cpp;">Complex operator-(Complex& a, Complex& b)
{
Complex ret;
ret.real = a.real - b.real;
ret.image = a.image - b.image;
return ret;
}</pre></div>
<p class="maodian"></p><h2>3. 流运算符重载</h2>
<p class="maodian"></p><h3>3.1 输出运算符<<重载</h3>
<p>输出运算符<code><<</code>的重载需要特别注意返回类型和参数类型。代码中实现了这一功能:</p>
<div class="jb51code"><pre class="brush:cpp;">ostream& operator<<(ostream& cout, Complex& other)
{
cout << other.real << "+" << other.image << "i";
return cout;
}</pre></div>
<p>这里有几个关键点:</p>
<ol><li><span>返回类型必须是<code>ostream&</code>(引用),这样才能支持链式输出如<code>cout << a << b << c;</code></span></li><li><span>第一个参数是<code>ostream&</code>类型,通常是<code>cout</code>或其变体</span></li><li><span>第二个参数是要输出的对象</span></li><li><span>函数返回输出流对象本身,使链式操作成为可能</span></li></ol>
<p>如果返回<code>void</code>类型,将无法实现链式输出。这是因为<code>cout << c << endl</code>会被解析为<code>(cout << c) << endl</code>,如果<code>cout << c</code>返回<code>void</code>,那么<code>void << endl</code>就是非法的。</p>
<p class="maodian"></p><h3>3.2 避免不必要的拷贝</h3>
<p>使用<code>const Complex& other</code>而不是<code>Complex other</code>的好处:避免拷贝构造。当对象较大时,传引用可以显著提高性能。正确的声明应该是:</p>
<div class="jb51code"><pre class="brush:cpp;">ostream& operator<<(ostream& cout, const Complex& other);</pre></div>
<p>这里的<code>const</code>确保不会意外修改对象状态。</p>
<p class="maodian"></p><h2>4. 自增运算符重载</h2>
<p>自增运算符有前置和后置两种形式,需要分别处理。</p>
<p class="maodian"></p><h3>4.1 前置自增运算符</h3>
<div class="jb51code"><pre class="brush:cpp;">Complex& operator++()//前置++
{
this->real += 1;
return *this;
}</pre></div>
<p>前置++返回<strong>引用</strong>,这是为了保持与内置类型一致的行为。这样<code>++a</code>本身可以作为左值使用</p>
<p class="maodian"></p><h3>4.2 后置自增运算符</h3>
<div class="jb51code"><pre class="brush:cpp;">Complex operator++(int)//后置++
{
Complex a = *this;//保存原始值
this->real += 1;
return a;//返回原始值
}</pre></div>
<p>后置++通过<strong>int参数</strong>与前置版本区分,这个参数仅用于区分,并不实际使用。它返回的是<strong>值</strong>而不是引用,因为返回的是局部对象,不能返回引用。</p>
<p>注释中提到的,后置++返回临时对象,这个对象在函数结束后会被销毁,所以不能返回引用。</p>
<p class="maodian"></p><h2>5. 赋值运算符重载</h2>
<p>赋值运算符重载需要特别注意<strong>深拷贝</strong>和<strong>自赋值</strong>问题。</p>
<div class="jb51code"><pre class="brush:cpp;">Jeff& operator=(Jeff &a)
{
if (age)
{
delete age;
age = NULL;
}
age = new int;//分配新内存
*age = *a.age;
return *this;
}</pre></div>
<p>这里有几个重要考虑:</p>
<ol><li><strong>检查自赋值</strong>:虽然代码中没有显式检查,但<code>a = a</code>这样的自赋值应该安全处理</li><li><strong>释放旧资源</strong>:在分配新资源前释放已有资源,防止内存泄漏</li><li><strong>深拷贝</strong>:创建新内存并复制内容,而不是简单复制指针</li><li><strong>返回引用</strong>:支持链式赋值<code>a = b = c</code></li></ol>
<p>改进版本应该包含自赋值检查:</p>
<div class="jb51code"><pre class="brush:cpp;">Jeff& operator=(const Jeff &a)
{
if (this != &a) { // 自赋值检查
if (age) {
delete age;
}
age = new int(*a.age);
}
return *this;
}</pre></div>
<p class="maodian"></p><h2>6. 关系运算符重载</h2>
<p>关系运算符重载通常返回<code>bool</code>值,用于比较对象。</p>
<div class="jb51code"><pre class="brush:cpp;">bool operator==(const Point& a)const
{
return this->x == a.x && this->y == a.y;
}
bool operator<(const Point& a)const
{
int c = x * x + y * y;
int d = a.x * a.x + a.y * a.y;
return c < d;
}</pre></div>
<p>代码中通过比较点到原点的距离来定义<code><</code>运算符,这是一种常见的做法。注意这些函数被声明为<code>const</code>,因为它们不应该修改对象状态。</p>
<p class="maodian"></p><h2>7. 函数调用运算符重载</h2>
<p>函数调用运算符<code>()</code>的重载创建了所谓的<strong>仿函数</strong>(functor)。</p>
<div class="jb51code"><pre class="brush:cpp;">int operator()(int a, int b)
{
data++;
return a + b + data;
}</pre></div>
<p>仿函数比普通函数更灵活,因为它们可以保持状态。如你的示例所示,每次调用都会增加<code>data</code>的值,这是普通函数无法做到的。</p>
<p>仿函数可以记录调用过程中的状态,比普通函数更加灵活,常用于STL算法中的定制行为</p>
<p class="maodian"></p><h2>8. 运算符重载的规则与最佳实践</h2>
<p class="maodian"></p><h3>8.1 可重载的运算符</h3>
<p>C++允许重载大部分运算符,包括:</p>
<ul><li>算术运算符:<code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>%</code></li><li>关系运算符:<code>==</code>, <code>!=</code>, <code><</code>, <code>></code>, <code><=</code>, <code>>=</code></li><li>逻辑运算符:<code>&&</code>, <code>||</code>, <code>!</code></li><li>赋值运算符:<code>=</code>, <code>+=</code>, <code>-=</code>, <code>*=</code>, <code>/=</code></li><li>下标运算符:<code>[]</code></li><li>函数调用运算符:<code>()</code></li><li>流运算符:<code><<</code>, <code>>></code></li><li>自增自减:<code>++</code>, <code>--</code></li></ul>
<p class="maodian"></p><h3>8.2 不可重载的运算符</h3>
<p>有些运算符不能重载,包括:</p>
<ul><li>成员访问运算符:<code>.</code></li><li>成员指针访问运算符:<code>.*</code></li><li>作用域解析运算符:<code>::</code></li><li>条件运算符:<code>?:</code>(三目运算符)</li><li><code>sizeof</code>运算符</li><li><code>typeid</code>运算符</li></ul>
<p class="maodian"></p><h3>8.3 最佳实践</h3>
<ol><li><strong>保持语义一致性</strong>:重载的运算符应该保持与内置类型相似的语义</li><li><strong>考虑返回值类型</strong>:<ul><li>算术运算符通常返回新对象</li><li>复合赋值运算符通常返回引用</li><li>关系运算符返回bool</li></ul></li><li><strong>正确处理常量性</strong>:不修改对象的函数应声明为<code>const</code></li><li><strong>遵循三/五法则</strong>:如果定义了拷贝构造函数、拷贝赋值运算符、析构函数中的一个,通常需要定义其他相关函数</li></ol>
<p class="maodian"></p><h2>9. 总结</h2>
<p><span>运算符重载是C++面向对象编程的重要特性,它让自定义类型能够以更自然的方式集成到语言中。通过合理使用运算符重载,我们可以编写出更加直观、易维护的代码。</span></p>
<p>关键要点:</p>
<ol><li>运算符重载的本质是函数重载,遵循函数重载的规则</li><li>选择成员函数还是全局函数形式取决于具体需求</li><li>流运算符<code><<</code>和<code>>></code>通常重载为全局友元函数</li><li>赋值运算符需要处理自我赋值和深拷贝问题</li><li>前置和后置自增/自减运算符通过参数区分</li><li>函数调用运算符重载创建仿函数,可以保持状态</li></ol>
<p><strong>合理使用运算符重载可以极大提高代码的可读性和易用性,但也要避免滥用,保持运算符的直观语义。当你面对自定义类型需要类似内置类型的操作时,运算符重载是一个强大的工具。</strong></p>
<p class="maodian"></p><h2>完整代码(杂糅版选取复制):</h2>
<div class="jb51code"><pre class="brush:cpp;">#include<iostream>
using namespace std;
#include<string>
int main()
{
//1.加法运算符
int a = 520;
int b = 1314;
cout << a + b<<endl;
//2.字符串拼接
string c = "520";
string d = "1314";
cout << c + d << endl;
}
复数类:
class Complex
{
friend ostream& operator<<(ostream& cout, Complex& other);
friend Complex operator+(Complex& a, Complex& b);
friend Complex operator-(Complex& a, Complex& b);
public:
Complex() :real(0), image(0)
{
}
Complex (int real, int image)
{
this->real = real;
this->image = image;
}
//Complex operator+(Complex& other)//加了operator后可以进行a+b操作,这个是成员函数形式
//{
// Complex ret;
// ret.real = this->real + other.real;//this是指针用->表示,other是引用对象用.表示
// ret.image = this->image + other.image;
// return ret;
//}
void print()
{
cout << real << "+" << image<<"i" << endl;//cout传进来了
}
private:
int real;
int image;
};
Complex operator+(Complex& a,Complex&b)//加了operator后可以进行a+b操作,全员函数重载+改-就变-了
{
Complex ret;
ret.real = a.real+b.real;//this是指针用->表示,other是引用对象用.表示
ret.image = a.image+b.image;
return ret;
}
Complex operator-(Complex& a, Complex& b)//加了operator后可以进行a+b操作,全员函数重载+改-就变-了
{
Complex ret;
ret.real = a.real - b.real;//this是指针用->表示,other是引用对象用.表示
ret.image = a.image - b.image;
return ret;
}
//void operator<<(ostream& cout, Complex& other)//void 不行
//{
// cout << other.real << "+" << other.image<<"i" << endl;
//}
ostream& operator<<(ostream& cout, Complex& other)//我左移后需要不断的输出所以要返回ostream可以继续加,实现链式输出!!!
{
cout << other.real << "+" << other.image << "i";
return cout;
}//补充知识点为什么ostream要加&,因为cout是ostream类型的函数,全局只有一个并且不希望外部调用也不希望内部调用,调用的话会自动删除!!!可以用f12看拷贝那一块的源码
//去掉&后会自动调用拷贝函数,然后拷贝函数就会被删除就无法调用了!!!
int main()
{
Complex a(10,20);
Complex b(5,8);
Complex c = a + b; //a.add(b);
Complex d = a - b;
//c.print();
//d.print();
////左移函数重载
cout << c << endl;//因为左移函数没有返回值void类型,我们要返回ostream这个类型才可以继续进行返回操作
return 0;
}
函数的递增
class Complex
{
friend ostream& operator<<(ostream& cout, const Complex &other);//Complex other每次都会走拷贝构造,已知这个不能修改,我们加一个&就可以避免拷贝构造了
friend Complex operator+(Complex& a, Complex& b);
friend Complex operator-(Complex& a, Complex& b);
private:
int real;
int image;
public:
Complex() :real(0), image(0)
{
}
Complex(int real, int image)
{
this->real = real;
this->image = image;
}
Complex& operator++()//前置++,不加引用就会生成新的对象,加了之后就是指向自己的
{
this->real += 1;
return *this ;
}
Complex operator++(int)//后置++,加引用就会返回临时的变量但是我,不想要这个临时的我要本身的,所以不加&让编译器自己生成一个拷贝函数
{
Complex a = *this;//等于对象本身,临时的变量会被析构掉所以会返回随机值
this->real += 1;
return a;//返回原来的对象
}
};
ostream& operator<<(ostream& c,const Complex& a)
{
cout << a.real << "+" << a.image << "i";
return c;
}
int main()
{
int x = 1;
cout << ++x<<endl;
cout << ++x<<endl;
Complex a(10, 20);
/*++a;*/
//cout << ++a << endl;
//cout << ++(++a) << endl;
//cout << a << endl;//第一++成功了,再调用一次就会生成新的对象与原来的a没有关系了所以要加引用确定原本对象;
cout << a++ << endl;
cout << (a++)++ << endl;
cout << a << endl;//加了&后这边就会报错为什么因为a的返回值是一个可修改的左值,所以我们可以用const去修饰这个返回值,这样就可以避免这样的事情发生,这样也是规范的写法
}
赋值重载运算
class Jeff
{
public:
Jeff():age(NULL)
{
};
Jeff(int age)
{
this->age = new int;
*(this->age) = age;
}
int *age;
~Jeff()
{
if (age != NULL)
{
delete age;
age = NULL;
}
}
Jeff& operator=(Jeff &a)
{
if (age)
{
delete age;
age = NULL;
}
age = new int;//新的内存,析构不会析构到这一快
*age = *a.age;
return *this;
}
};
int main()
{
Jeff a(1);
Jeff b(2);
cout << *(a.age) << endl;//内存泄漏
cout << *(b.age) << endl;
a = b;//a b两个对象本来有那个内存的,第一次a调用时析构函数把a的对象销毁了,第二次b调用时由于两个都指向同一块地址,b调用完成后又一次析构,这时就造成二次析构了
cout << *(a.age) << endl;
cout << *(b.age) << endl;
Jeff c(3);
a = b = c;
return 0;
}
关系运算符重载
class Point
{
int x, y;
public:
Point():x(0), y(0)
{
};
Point(int x, int y)
{
this->x = x;
this->y = y;
}
bool operator==(const Point& a)const
{
return this->x == a.x && this->y == a.y;
}
bool operator<(const Point& a)const
{
int c = x * x + y * y;
int d = a.x * a.x + a.y * a.y;
return c < d;
}
bool operator>(const Point& a)const
{
if (*this==a)
{
return false;
}
else if (*this < a)
{
return false;
}
else {
return true;
}
}
};
int main()
{
Point a(2,6);
Point b(1,8);
if (a > b)
{
cout << "a到原点的距离大于b" << endl;
}
else if (a == b)
{
cout << "a到原点的距离等于b" << endl;
}
else {
cout << "a到原点的距离小于b" << endl;
}
return 0;
}
函数调用运算符重载
class HJM
{
private:
int data;
public:
HJM() :data(0)
{
};
int operator()(int a, int b)
{
data++;
return a + b + data;
};//仿函数调用可以记录当前的状态
};
int Add(int a, int b)
{
return a + b;
}
int main()
{
HJM add;
cout << add(10, 5) << endl;
cout << add(10, 5) << endl;
cout << add(10, 5) << endl;
cout << add(10, 5) << endl;
cout << add(10, 5) << endl;
cout << add(10, 5) << endl;
cout << add(10, 5) << endl;
cout << Add(10, 5)<< endl;
cout << Add(10, 5) << endl;
cout << Add(10, 5) << endl;
cout << Add(10, 5) << endl;
cout << Add(10, 5) << endl;
return 0;
}</pre></div>
<p>到此这篇关于C++运算符重载的实现示例的文章就介绍到这了,更多相关C++运算符重载内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
<div class="art_xg">
<b>您可能感兴趣的文章:</b><ul><li>C++ 运算符重载的使用</li><li>C++之运算符重载的实例(日期类实现方式)</li><li>C++类和对象之运算符重载解读</li><li>C++类中的运算符重载过程</li><li>C++实现Date类各种运算符重载的示例代码</li><li>关于C++的重载运算符和重载函数</li><li>C++重载的奥义之运算符重载详解</li><li>C++实现重载矩阵的部分运算符</li><li>一文搞懂C++中的运算符重载</li></ul>
</div>
</div>
<!--endmain-->
頁:
[1]