C++ operator

重载运算符和重载函数

C++ 允许在同一作用域中的某个函数运算符指定多个定义,分别称为函数重载运算符重载。重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。

函数重载

函数重载指的是可以有多个同名的函数,因此对名称进行了重载,它们完成相同的工作,但使用不同的参数列表。这个比较容易理解与操作。

运算符重载

重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。

Box operator+(const Box&);

声明加法运算符用于把两个 Box 对象相加,返回最终的 Box 对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数,如下所示:

Box operator+(const Box&, const Box&);

感觉这个例子说的太浅显了,搬运liitdar对这部分的详细解释

Why

首先介绍为什么要对赋值运算符“=”进行重载。某些情况下,当我们编写一个类的时候,并不需要为该类重载“=”运算符,因为编译系统为每个类提供了默认的赋值运算符“=”,使用这个默认的赋值运算符操作类对象时,该运算符会把这个类的所有数据成员都进行一次赋值操作。按如下命令进行复现,首先在命令行中输入

touch testOperator.cpp

然后用编辑器,比如用vim写入如下代码

#include <iostream>

using namespace std;

class ClassA
{
public:
int a;
int b;
int c;
};

int main()
{
ClassA obj1;
obj1.a = 1;
obj1.b = 2;
obj1.c = 3;

ClassA obj2;
obj2 = obj1;

cout << "obj2.a is: " << obj2.a << endl;

return 0;
}

采用g++编译器进行编译后,得到的结果如下所示

通过上述结果能够知道:通过使用系统默认的赋值运算符“=”,可以让对象 obj2 中的所有数据成员的值与对象 obj1 相同。这种情况下,编译系统提供的默认赋值运算符可以正常使用。 但是,在下面的示例中,使用编译系统提供的默认赋值运算符,就会出现问题了。 示例代码内容如下(在同一个文件里进行修改):

#include <iostream>
#include <string.h>

using namespace std;

class ClassA
{
public:
ClassA()
{

}

ClassA(const char* pszInputStr)
{
pszTestStr = new char[strlen(pszInputStr) + 1];
strncpy(pszTestStr, pszInputStr, strlen(pszInputStr) + 1);
}
virtual ~ClassA()
{
delete pszTestStr;
}
public:
char* pszTestStr;
};

int main()
{
ClassA obj1("liitdar");

ClassA obj2;
obj2 = obj1;

cout << "obj2.pszTestStr is: " << obj2.pszTestStr << endl;
cout << "addr(obj1.pszTestStr) is: " << &obj1.pszTestStr << endl;
cout << "addr(obj2.pszTestStr) is: " << &obj2.pszTestStr << endl;

return 0;
}

编译并运行上述代码,结果如下:

上述错误信息表明:当对象 obj1 和 obj2 进行析构时,由于重复释放了同一块内存空间,导致程序崩溃报错。在这种情况下,就需要我们重载赋值运算符"="了。 修改一下前面出错的示例代码,编写一个包含赋值运算符重载函数的类,修改后的代码内容如下:

#include <iostream>
#include <string.h>

using namespace std;

class ClassA
{
public:
ClassA()
{

}
ClassA(const char* pszInputStr)
{
pszTestStr = new char[strlen(pszInputStr) + 1];
strncpy(pszTestStr, pszInputStr, strlen(pszInputStr) + 1);
}
virtual ~ClassA()
{
delete pszTestStr;
}
// 赋值运算符重载函数
ClassA& operator=(const ClassA& cls)
{
// 避免自赋值
if (this != &cls)
{
// 避免内存泄露
if (pszTestStr != NULL)
{
delete pszTestStr;
pszTestStr = NULL;
}

pszTestStr = new char[strlen(cls.pszTestStr) + 1];
strncpy(pszTestStr, cls.pszTestStr, strlen(cls.pszTestStr) + 1);
}

return *this;
}

public:
char* pszTestStr;
};

int main()
{
ClassA obj1("liitdar");

ClassA obj2;
obj2 = obj1;

cout << "obj2.pszTestStr is: " << obj2.pszTestStr << endl;
cout << "addr(obj1.pszTestStr) is: " << &obj1.pszTestStr << endl;
cout << "addr(obj2.pszTestStr) is: " << &obj2.pszTestStr << endl;

return 0;
}

编译并运行上述代码,结果如下:

对于上述代码,有以下几点需要说明:

1, 当为一个类的对象赋值(可以用本类对象为其赋值,也可以用其它类型的值为其赋值)时,该对象(如本例的 obj2)会调用该类的赋值运算符重载函数,进行具体的赋值操作。如上述代码中的“obj2 = obj1;”语句,用 obj1 为 obj2 赋值,则会由 obj2 调用 ClassA 类的赋值运算符重载函数;

2, 下方语句和语句“ClassA obj2 = obj1;”在调用函数上是有区别的:前者第一句是对象 obj2 的声明及定义,调用类 ClassA 的无参构造函数,所以“obj2 = obj1;”一句是在对象 obj2 已经存在的情况下,用 obj1 来为 obj2 赋值,调用的是赋值运算符重载函数;而后者,是用 obj1 来初始化 obj2,调用的是拷贝构造函数。拷贝构造函数的语句样式为“ClassA(const ClassA& cls)”,

ClassA obj2;
obj2 = obj1;

3, 当程序没有显式地提供一个以“本类或本类的引用”为参数的赋值运算符重载函数时,编译器会自动生成一个默认的赋值运算符重载函数(即默认赋值运算符)。

VS Eigen configure

打开VS,进入到项目的属性页面, 如下所示

将Eigen3文件包含到里面即可。

参考

[1] 菜鸟教程

[2] C++编程语言中赋值运算符重载函数(operator=)介绍

知识共享许可协议