C++拷贝构造器、运算符重载1
#include <iostream>
using namespace std;
class Str //模仿string类
{
public:
Str(const char* ptr="") //接收字符串来初始化对象,不修改参数所以const,用默认参数来兼容无参构造器(无参生成空字符串)
{
arr = new char[strlen(ptr) + 1]; //创建和参数长度一样大的空间,动态分配的内存位于堆中
strcpy(arr, ptr); //字符串赋值
}
~Str()
{
delete[] arr; //释放堆中的数组
}
Str(const Str& another) //拷贝构造器,使用一个已存在的对象初始化新的对象,默认生成,会将形参的所有字段拷贝用来初始化,格式固定: 类名(const 类名& 引用名),使用了引用,another就是主调函数中的实参对象本身
{
//如果不手动定义拷贝构造器,默认生成的拷贝构造器会对所有字段进行成员赋值(浅拷贝),执行arr=another.arr,这对基础类型以及结构不会产生问题,但指针赋值会使两个对象使用同一块动态内存空间
arr = new char[strlen(another.arr) + 1]; //在类的函数中不仅可以调用当前对象的私有字段,也可以调用其他同类型对象的私有字段,对象中有指针字段时需要重写拷贝构造器
strcpy(this->arr, another.arr); //过程和使用字符串初始化类似,this是指向当前对象的指针,*this就是当前Str对象(的别名),直接调用arr就相当于(*this).arr
cout << "copy" << endl;
}
Str& operator=(const Str& another) //赋值运算符重载,将一个存在的对象赋值给一个存在的对象,默认生成,和默认的拷贝构造器一样执行成员赋值,只拷贝字段不创建额外生成的动态内存空间,固定格式: 类名& operator=(const 类名& 引用名),返回当前对象的引用
{
if (this == &another) //this为指向当前对象的指针,another为另一个对象的引用即另一个Str本身,判断当前对象的地址(this的值)和另一个对象的地址(&another)相同即自己赋值自己,直接返回
return *this; //*this为当前对象,将当前对象的引用返回,不能返回another因为声明const
delete[]arr; //先释放已创建的内存
arr = new char[strlen(another.arr) + 1];
strcpy(this->arr, another.arr); //和上面的构造方法相同
cout << "overload" << endl;
}
//构造器、析构器、拷贝构造器、赋值运算符重载、地址运算符重载(C++11增加移动构造器、移动赋值运算符重载),这五个函数在没有手动定义的情况下会默认生成;默认的地址运算符重载会返回对象所在的地址
const char* c_str()
{
return arr; //将私有的数组指针返回并声明const禁止修改
}
size_t find(char ch)
{
char* ptr = strchr(arr, ch);
return ptr ? ptr - arr : (size_t)-1;
}
Str operator+(const Str& another) //模拟字符串相加,s1+s2返回一个新的字符串,内容为s1拼接s2
{
Str res; //生成新的对象
res.~Str(); //释放无参构造器生成的char[1]数组
res.arr = new char[strlen(this->arr)+strlen(another.arr) + 1]; //两个对象字符串长度之和,留一个\0
strcpy(res.arr, this->arr); //先拷贝this
strcat(res.arr, another.arr); //使用strcat拼接
return res; //res对象是自动变量,在返回后释放,所以不能返回res的引用,返回操作实际执行了一次拷贝构造器,在函数外(具体位置取决于实现)用res初始化了一个临时空间交给主调函数
}
bool operator>(const Str& another) //通过strcmp实现对象的比较
{
return strcmp(arr, another.arr) > 0;
}
bool operator<(const Str& another)
{
return strcmp(arr, another.arr) < 0;
}
bool operator==(const Str& another) //三种运算符分别重载
{
return strcmp(arr, another.arr) == 0;
}
char& operator[](int index) //实现对象s1[i]取元素
{
return arr[index]; //返回元素本身可修改,就像char数组[i]一样使用
//注意这里的返回类型为char&,如果是char则返回字符的备份,备份无法修改不能改变下标的字符,
}
char operator[](int index)const //const成员函数,如果调用对象为const时就不能返回下标字符本身因为可能会被修改,所以去掉&变成按值传递,返回char的值/下标字符的备份,格式为:函数头 const {函数体},后续详细介绍
{
return arr[index]; //返回的内容没有变化,区别只在返回值和const
}
private:
char* arr;
};
int mainclass2()
{
Str s,s1("s1"),s2("s2");
cout << s1.c_str() << endl;
cout << s1.find('1') << endl;
Str s3(s1); //拷贝构造器
Str s4 = s2; //也是拷贝构造器,声明阶段初始化值,和赋值不同
cout << s4.c_str() << endl;
Str s5;
s5 = s3; //赋值,相当于s5.operator=(s3)
s1 + s2; //s1+s2相当于s1.operator+(s2),返回结果是匿名空间,在c++代码层面无法访问这个没有标识符的空间
Str s6 = s1 + s2;//将返回的空间标识为s6,所以过程中发生了一次无参构造、一次拷贝构造
cout << s6.c_str() << endl;
Str s7;
s7 = s3 + s4; //相当于s7.operator=(s3.operator+(s4)),运算符重载遵循运算符的优先级
Str sarr[10] = { Str("ele1"),Str("ele2") }; //对象数组,和普通数组初始化一样用{},如果初始化的项数小于数组大小,剩下的元素会调用无参构造器,所以声明对象时应保留无参构造器
return 0;
}
Str returnclass()
{
return Str("匿名对象"); //先通过有参构造器创建对象,在函数return该对象时就会发生拷贝构造,将返回的对象作为拷贝构造器的参数,在临时空间使用拷贝构造创建临时对象,之后被调函数结束,自动变量释放,主调函数访问临时空间
//和返回基本数据类型同理,return 1是返回1的备份,return 对象 是返回对象的备份,通过拷贝构造器创建备份
}
class ClassNoCopy
{
public:
ClassNoCopy() {}
private:
ClassNoCopy(const ClassNoCopy& another) {} //将拷贝构造器私有,禁止外部调用,手动声明后不会再默认生成公有的拷贝构造器
ClassNoCopy& operator=(const ClassNoCopy& another) = delete; //使用delete关键字禁用该成员函数,格式:任意成员函数的函数头 = delete,当主调函数调用这个成员函数时会报错
int val;
};
ClassNoCopy funcCNC()
{
ClassNoCopy c1;
//报错:ClassNoCopy c2(c1); //拷贝构造器私有不可访问
//报错:ClassNoCopy c3 = c1; //声明时初始化同样调用拷贝构造器,私有不可访问
ClassNoCopy c4;
//报错:c4 = c1; //将一个对象赋值给另一个对象,赋值运算符重载不可访问
//报错:return c1; //返回对象调用拷贝构造器,私有不可访问
}
class ClassOperatorOverload
{
public:
int data;
ClassOperatorOverload(int val = 0)
{
data = val;
}
//ClassOperatorOverload& operator=(const ClassOperatorOverload& another)
//{
// /*省略自己赋值自己的判断*/
// data = another.data;
// return *this; //返回当前对象本身,可以实现链式赋值 c1 = c2 = c3 将c3赋值给c2后返回c2,再将c2赋值给c1(等号结合律右到左)。也支持(c1 =c2)=c3 将c2赋给c1返回c1,再将c3赋给c1返回c1
//}
//const ClassOperatorOverload& operator=(const ClassOperatorOverload& another) //将返回声明为const,禁止将返回值作为左值,表达式 c1 = c2 = c3 仍然正常执行(=运算符从右向左执行),但(c1=c2)=c3报错,因为c1=c2返回的c1的引用为const引用
//{
// /*省略自己赋值自己的判断*/
// data = another.data;
// return *this;
//}
void operator=(const ClassOperatorOverload& another) //不返回对象,这时c1=c2正常,c1=c2=c3报错因为c2=c3无返回,而只改变返回值类型不构成重载,但都视为赋值运算符的函数,不再生成默认的
{
// /*省略自己赋值自己的判断*/
data = another.data;
}
};