1.操作符重载:赋予某一类型,可以使用某一操作符的能力
#include <iostream>
using namespace std;
class MyClass
{
private:
int mValue;
public:
int getValue(); // 声明方法
MyClass(int value); // 声明构造方法
};
int MyClass::getValue()
{
return mValue;
}
MyClass::MyClass(int value)
{
mValue = value;
}
int main(int argc, const char * argv[]) {
// MyClass /*myClass1.*/operator+(MyClass myClass2) {this}
int a = 20;
int b = 30;
int c = a + b;
MyClass myClass1(20);
MyClass myClass2(30);
MyClass myClass = myClass1 + myClass2; // 会报错,C++不支持这两个对象相加
return 0;
}
操作符重载语法:MyClass myClass1.operator+(MyClass myClass2),返回值类型是MyClass,myClass1是类本身,operator+看作类的方法,myClass2看作方法的参数,在方法operator+里面。通过指针this表示myClass1,依据此语法,实现操作符重载,在声明时,该语法写为:MyClass operator+(MyClass myClass2)则,方法operator+的写法:
MyClass MyClass::operator+(MyClass c)
{
MyClass myClass(0);
myClass.mValue = mValue + c.mValue; // 式中的mValue可以写为this.mValue
return myClass;
}
//实现了 MyClass myClass = myClass1 + myClass2
方法operator-
MyClass MyClass::operator-(MyClass c)
{
MyClass myClass(0);
myClass.mValue = mValue - c.mValue;
return myClass;
}
//实现了 MyClass myClass = myClass1 - myClass2
MyClass myClass = myClass1 + myClass2 + myClass1 的实现:
第一步:myClass1.operator+(myClass2 + myClass1)
第二步:myClass1.operator+(myClass2.operator+(myClass1))
2.函数形式的操作符重载
//使用函数形式完成“*”的操作符重载/
#include <iostream>
using namespace std;
class MyClass
{
private:
int mValue;
public:
int getValue();
void setValue(int value);
MyClass(int value);
MyClass operator+(MyClass c);
MyClass operator-(MyClass c);
};
int MyClass::getValue()
{
return mValue;
}
void MyClass::setValue(int value)
{
mValue = value;
}
// 此处定义的不是类的方法,而是函数,所以就不能用this指针,那么传入参数就应该是两个,即乘法的左侧和右侧
MyClass operator*(MyClass &left, MyClass &right) // 加上符号“&”,表示以引用的方式传参,这样避免的值的直接传递,效率更高
{
MyClass myClass(0);
myClass.setValue(left.getValue() * right.getValue());
return myClass;
}
MyClass MyClass::operator+(MyClass c)
{
MyClass myClass(0);
myClass.mValue = mValue + c.mValue;
return myClass;
}
int main(int argc, const char * argv[]) {
// 函数形式的操作符重载,其中operator是关键字,所以operator+就可以用+代替
MyClass myClass1(20);
MyClass myClass2(30);
MyClass myClass = myClass1 + myClass2 + myClass1;
cout << myClass.getValue() << endl; // 70
myClass = myClass1 - myClass2;
cout << myClass.getValue() << endl; // -10
myClass = myClass + myClass1 * myClass2;
cout << myClass.getValue() << endl; // 590,优先级不变
return 0;
}
3.操作符重载的限制
操作符重载的限制
1. 重载后的操作数至少有一个是用户自定义类型,为了防止用户重载基本类型的操作符
2. 不能改变操作符本身的特性,例如,不能将二元操作符变成一元或三元操作符
3. 不能修改操作符的优先级(先乘除后加减)
4. 不能创建新的操作符
5. 并不是所有的操作符都可以重载,例如sizeof、指针(*),?:(条件操作符)等
6. 大多数可以重载的操作符都可以使用成员方法和非成员方法(函数形式)重载,但,赋值:=,函数调用:(),下标运算符:[ ],通过指针访问成员:-> 只能通过成员方法重载,成员方法:在类、结构体、共用体中进行重载。
#include <iostream>
using namespace std;
struct MyStruct
{
int value = 20;
/* int operator%(int v) // 限制2,参数只能有一个,一个参数表示二元操作符
{
return value % v;
}*/
void operator=(int v)
{
value = v;
}
};
int operator+(int left, MyStruct &myStruct) // 限制1
{
return left + myStruct.value;
}
int operator%(MyStruct &myStruct, int v) // 限制2,函数方法重载(非成员方法),用两个参数表示二元操作符
{
return myStruct.value % v;
}
/*void operator=(MyStruct &myStruct, int v)
{
myStruct.value = v;
}*/
int main(int argc, const char * argv[])
{
int a = 1+2;
MyStruct myStruct;
int x = 30 + myStruct;
cout << x << endl; // 50
cout << myStruct % 3 << endl;
//int a = myStruct +&^%$ 4; 限制4
myStruct = 40;
cout << myStruct.value << endl;
return 0;
}
4.友元函数
友元函数,一个非成员方法,这个函数可以使用类/结构体中的私有成员。
1. 在类(结构体)中需要定义友元函数的原型(前面加friend)
2. friend只能在友元函数的原型加,不能在友元函数的实现部分加friend
3. 尽管友元函数的原型在类中定义,但不能像调用类成员方法一样调用友元函数
#include <iostream>
using namespace std;
class MyClass
{
private:
int mValue;
public:
MyClass(int value)
{
mValue = value;
}
void print()
{
cout << mValue << endl;
}
MyClass operator*(int right) // 这种方式重载操作符有一个缺点:MyClass只能在乘号左侧
{
return MyClass(mValue * right);
}
friend MyClass operator*(int, MyClass); // 用friend关键字声明函数原型,此时非成员方法也能访问私有成员mValue了
};
MyClass operator*(int left, MyClass right) // 实现操作符重载,同时MyClass在乘号右侧
{
return left * right.mValue; // 使用friend声明后,可以访问私有成员mValue
}
int main(int argc, const char * argv[]) {
MyClass result = 20 * MyClass(30) ;//这是MyClass在右侧的情况,此时按照参数类型,会先使用非成员方法,在非成员方法中,将两个参数位置互换,这样就符合了成员方法,进而使用成员方法进行计算
result.print();
return 0;
}
5.友元函数与<<操作符重载
#include <iostream>
using namespace std;
class MyClass
{
private:
int mValue;
public:
MyClass(int value)
{
mValue = value;
}
friend ostream& operator<<(ostream &os, MyClass &myClass);
};
ostream& operator<<(ostream &os, MyClass &myClass) // ostream:输出流
{
return os << myClass.mValue;
}
int main(int argc, const char * argv[]) {
// 友元函数与<<操作符重载
MyClass myClass(20);
cout << myClass << "abc" << endl; // 对于操作符<<,myClass一定是右操作数,在成员方法中myClass必须是左操作数,所以需要用非成员方法对其进行重载
/*
((cout << myClass) << "abc") << endl // (cout << myClass)表达式的最终值应该是ostream类型,这样才能继续对右边的“abc”进行操作。所以operator<<的返回值应该是ostream&
*/
return 0;
}
6.类型转换(数值和对象之间的互转)
语法: operator typename( ) {... }
1. 转换函数必须是方法,不能是函数
2. 转换函数不能有返回值
3. 转换函数不能有参数
#include <iostream>
using namespace std;
class MyClass
{
private:
int mValue;
double mDValue;
public:
MyClass(int value)
{
mValue = value;
}
MyClass(double value)
{
mDValue = value;
}
operator int();
operator double()
{
return mDValue;
}
};
MyClass::operator int()
{
return mValue;
}
int main(int argc, const char * argv[]) {
// 类型转换(数值和对象之间的互转)
// 语法: operator typename() {... }
// 1. 转换函数必须是方法,不能是函数
// 2. 转换函数不能有返回值
// 3. 转换函数不能有参数
int n = 33.3;
MyClass myClass(30); // 将int类型的值转换为MyClass对象
int value = myClass; // 会自动调用operator int()
cout << value << endl;
MyClass myClass1(30.123);
double value1 = myClass1; // 会自动调用operator double()
cout << value1 << endl;
return 0;
}
7.类中静态变量和静态常量的初始化问题
main.cpp
#include <iostream>
#include "Test.h"
using namespace std;
int main(int argc, const char * argv[])
{
// 类中的静态变量
MyClass myClass; // 实例化类时,会执行类的构造方法,value变为200
cout << MyClass::value << endl; // 200
// 1. static变量不能直接在class中初始化
// 2. static变量不能在头文件中初始化
// 3. static变量可以在cpp文件中初始化,但初始化时不能指定static
// 4. static变量可以在类的构造方法中初始化
// 例外:类中的静态常量(static const)(必须是整型或枚举),可以在类中声明常量的同时初始化,也可以在cpp中初始化
return 0;
}
Test.cpp
#include "Test.h"
int MyClass::value = 100; // 对类中的静态成员进行初始化
// const int MyClass::max = 4321;
int MyClass::getValue()
{
return value;
}
MyClass::MyClass() // 类的构造方法
{
value = 200;
}
Test.h
#ifndef __class_static__Test__
#define __class_static__Test__
class MyClass // 这里并没有给类分配任何的内存空间,创建这个类的对象时,才会进行分配
{
public:
static int value; // 对于这个静态变量,如果在类中进行初始化,那系统就会给静态变量分配内存空间,这样的话就和类冲突了,所以不能用static int value = 10;
// 初始化静态的类的成员,需要在类的外部
static const int max = 4321; // 静态常量是例外
MyClass();
int getValue();
};
#endif /* defined(__class_static__Test__) */
8.复制构造方法
#include <iostream>
using namespace std;
class MyClass
{
private:
int mValue = 0;
public:
MyClass(int value)
{
mValue = value;
}
MyClass(const MyClass& myClass); // 复制构造方法
int getValue()
{
return mValue;
}
void setValue(int value)
{
mValue = value;
}
};
// 复制构造方法,如果用户自定义了复制构造方法,那么在类之间的赋值时,会调用自定义构造方法,而不是默认的
MyClass::MyClass(const MyClass& myClass) // 如果设置成内联的,那么赋值的等号右边作为参数传入
{
mValue = myClass.mValue;
cout << myClass.mValue << endl;
}
int main(int argc, const char * argv[])
{
// 复制构造方法
MyClass myClass(20);
MyClass newClass = myClass; // clone,复制当前的对象,连同里面的值,无论里面的是私有的还是公共的
cout << newClass.getValue() << endl; // 打印 20
newClass.setValue(100);
cout << myClass.getValue() << endl; // 20
cout << newClass.getValue() << endl; // 100
cout << "-------" << endl;
// 触发复制构造方法的四种情况
// 1
MyClass myClass1(10);
MyClass newMyClass1(myClass1); // 打印 10 ,newMyClass1和myClass1是类创建的对象
// 2
MyClass myClass2(30);
MyClass newMyClass2 = myClass2; // 打印 30
// 3
MyClass myClass3(50);
MyClass newMyClass3 = MyClass(myClass3); // 50 MyClass是复制构造方法,和构造方法的区别是其以类对象作为传入参数
// 4
MyClass myClass4(60);
MyClass *pMyClass = new MyClass(myClass4); // 60
return 0;
}
9.使用复制构造方法可能会带来的问题
#include <iostream>
using namespace std;
class MyClass
{
private:
string mName;
char *mProductName;
public:
MyClass()
{
mName = "Bill Gates";
mProductName = new char[20];
strcpy(mProductName, "Surface Pro3");
}
string getName()
{
return mName;
}
char* getProductName()
{
return mProductName;
}
~MyClass() // 析构方法
{
delete [] mProductName;
}
MyClass(const MyClass& myClass) // 自定义的复制构造方法
{
mName = myClass.mName;
// error: mProductName = myClass.mProductName;
mProductName = new char[20];
strcpy(mProductName, myClass.mProductName);
}
};
int main(int argc, const char * argv[]) {
// 复制构造方法可能带来的问题
MyClass *pMyClass1 = new MyClass();
MyClass *pMyClass2 = new MyClass(*pMyClass1); // 这里的*是取地址值,即类对象
// delete pMyClass1; 此句会执行析构方法,那么下面的第一句就不会再打印值了,都给释放了,
//想让它打印值,就需要自定义复制构造方法,让指针所指向的量,按照值去传递;如果不自定义复制构造方法,
//那么就会调用默认的方法,默认的方法传递指针值时,是通过多个指针指向相同地址实现的,
//并不是通过值传递,这样的话,一旦内存被释放,就会没值了
cout << pMyClass2->getProductName() << endl;
cout << pMyClass2->getName() << endl;
return 0;
}
10.类的静态成员方法
#include <iostream>
using namespace std;
class MyClass
{
public:
static int code;
static string getName()
{
return "Bill Gates";
}
static int getCode();
};
int MyClass::code = 20; // 类中的静态成员不能在类内初始化
int MyClass::getCode() // 在外部定义时,静态方法和普通方法看不出差别
{
return code;
}
int main(int argc, const char * argv[]) {
// 类中的静态成员方法
cout << MyClass::code << endl; // 对于静态成员来说,不需要对类进行实例化,直接打印值就能打印出来,对于类中的普通成员可以按占位符理解,不实例化是打印不出来值的
cout << MyClass::getName() << endl;
return 0;
}
11.函数/方法的参数和返回值应该返回对象,还是对象引用
方法/函数如何返回对象
1. 直接返回对象,如果对象很大,那么会消耗很多资源
2. 返回对象的引用,尽量不要返回函数内局部变量的引用
3. 返回对象的指针
#include <iostream>
using namespace std;
class MyClass
{
public:
int code = 20;
MyClass(const MyClass& myClass) // 参数类型是引用
{
code = myClass.code;
cout << "MyClass(const MyClass& myClass)" << endl;
}
MyClass()
{
}
};
void process(MyClass myClass)
{
}
MyClass mul2(MyClass myClass)
{
myClass.code = myClass.code * 2;
return myClass;
}
MyClass mul2_1(MyClass &myClass)
{
myClass.code = myClass.code * 2;
return myClass;
}
MyClass& mul2_2(MyClass &myClass)
{
myClass.code = myClass.code * 2;
return myClass;
}
int main(int argc, const char * argv[]) {
MyClass myClass1;
// MyClass myClass2 = mul2(myClass1); // 调用了两次复制构造方法,第一次是myClass1作为参数传入函数栈时,进行了复制,第二次等号赋值产生的
process(mul2(myClass1));
// MyClass myClass2 = mul2_1(myClass1); // 调用了一次复制构造方法,等号赋值产生的
// MyClass &myClass2 = mul2_2(myClass1); // 一次构造方法也没调用
// cout << myClass2.code << endl;
return 0;
}
12.操作符重载返回常量对象引用,还是对象引用
#include <iostream>
using namespace std;
class MyClass
{
public:
int code;
MyClass(int code)
{
this->code = code;
}
const MyClass& operator+(MyClass &myClass) // 返回值是常量引用,那么所返回的对象是不能修改的
{
myClass.code = code + myClass.code;
return myClass;
}
};
int main(int argc, const char * argv[]) {
// 操作符重载返回常量对象引用,还是对象引用
MyClass myClass1(20);
MyClass myClass2(30);
MyClass myClass = myClass1 + myClass2; // 加号重载后,二者相加所返回的值是myClass2,即,将myClass2赋值给了myClass,myClass2里面是50
cout << myClass.code << endl;
// cout << myClass2.code << endl; // 50
return 0;
}