目录
一、拷贝构造函数
1.1概念
只有一个形参,这个形参是对类类型对象的引用,在用存在的类类型对象创建时由编译器自动调用。
1.2特征
1.拷贝构造函数是创建与已经存在对象一模一样的新对象。
2.拷贝构造函数一般用const修饰。
3.拷贝构造函数是构造函数的一个重载形式。
4.拷贝构造函数的参数必须是类类型对象的引用,且参数只能有一个。
5.如果用户未定义,编译器会生成默认的拷贝构造函数。默认拷贝构造函数对内置类型对象按内存存储字节序完成浅拷贝,自定义类型是调用其自身的构造函数完成浅拷贝。
class Time
{
public:
Time()
{
_hour = 1;
_minute = 1;
_second = 1;
}
Time(const Time& t)
{
_hour = t._hour;
_minute = t._minute;
_second = t._second;
cout << "Time::Time(const Time&)" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定义类型
Time _t;
};
int main()
{
Date d1;
// 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数
// 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构
//造函数
Date d2(d1);
return 0;
}
6. 虽然编译器会生成默认的拷贝构造函数,但仅仅会完成浅拷贝,如果拷贝的函数涉及空间申请(如stack),则会出现错误(两个函数会申请到同一块空间,销毁时会出现同一块空间释放2次,导致程序崩溃)。
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 10)
{
_array = (DataType*)malloc(capacity * sizeof(DataType));
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_size = 0;
_capacity = capacity;
}
void Push(const DataType& data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
~Stack()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
size_t _size;
size_t _capacity;
};
int main()
{
Stack s1;
s1.Push(1);
s1.Push(2);
s1.Push(3);
s1.Push(4);
Stack s2(s1);
return 0;
}
上述代码中s2和s1申请了同一块空间,释放时导致程序崩溃。
二、赋值运算符重载
2.1运算符重载
1.C++为了增强代码的可读性,增加了运算符重载的概念,支持自定义函数的操作符运算。
2.运算符重载是一个有着特殊函数名的函数,有返回值,有参数、函数名等。
3.函数名为:operator加上要重载的操作符
4.标准模板:
返回值类型 operator操作符(参数)
注:
1.operator后的操作符不能自己创建,必须是已含的操作符。
bool operator@(const Date & d2)//非已含的操作符
{//报错
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
}
2.重载操作符必须有一个类类型的参数。
bool operator==()//参数过少
{
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
}
3.用于内置类型的运算符,不会随运算符重载改变其本身含义。
// 定义一个复数类
class Complex
{
public:
// 成员变量:实部和虚部
double real;
double imag;
// 构造函数
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i)
{
}
// 重载加法运算符,使得两个Complex对象可以相加
Complex operator+(const Complex& other) const
{
return Complex(real - other.real, imag - other.imag);//将减法重载成加法
}
// 为了方便打印,重载<<运算符
friend ostream& operator<<(std::ostream& os, const Complex& c)
{
os << c.real << " + " << c.imag << "i";
return os;
}
};
int main()
{
Complex num1(3, 4); // 创建复数3 + 4i
Complex num2(1, -1); // 创建复数1 - 1i
Complex sum = num1 + num2; // 使用重载的+运算符
int a = 2;
int b = 3 + a;
cout << "Sum: " << sum << endl; // 输出相加结果
cout << "b: " << b << endl;
return 0;
}
上图中,我故意将减法重载成加法,Sum执行的+是我重载的减法,但b执行的还是加法,说明内置类型的运算符不受重载运算符的影响。
4.类的成员函数重载时,形参比操作数少1,因为成员函数的第一个参数是隐藏的this(C++:类的对象模型和this指针中有介绍)。
bool operator==( Date & d2)//只有一个参数但有两个操作数
{
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
}
上面图片的代码在编译器眼中是下图这样
bool operator==( Date & d2)
{
return this->_year == d2._year
&& this->_month == d2._month
&& this->_day == d2._day;
}
5. (.*) (::) (sizeof) (?:) (.) 前面括号内的5个操作符不能重载。
2.2赋值运算符重载
2.2.1赋值运算符重载格式
1.参数类型:const T&,传递引用可以提高传参效率。
2.返回值:*this,返回引用可以提高返回的效率,有返回值是为了支持连续的赋值
3.要检验是否会自己给自己赋值
Date& operator=(const Date& d)//参数类型含有const,防止改变参数
{
if (this != &d)//检验是否会自己给自己赋值
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;//返回*this方便连续赋值
}
2.2.2赋值运算符重载要求
赋值运算符只能重载成类的成员函数不能重载成全局函数。用户如果不写赋值运算符重载,编译器会自动生成一个默认的赋值运算符重载,与全局的运算符重载冲突。
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
int _year;
int _month;
int _day;
};
Date& operator=(Date& left, const Date& right)//全局函数
{
if (&left != &right)
{
left._year = right._year;
left._month = right._month;
left._day = right._day;
}
return left;
}
2.2.3默认生成的赋值运算符重载
编译器默认生成的赋值运算符重载以值的方式逐字节拷贝。对于内置类型成员变量是直接赋值的(浅拷贝),对于自定义类型成员变量需要调用对应类的赋值运算符完成赋值。
注: 编译器自动生成的赋值运算符重载是浅拷贝,对于无空间申请的函数可以(如Date),对于有空间申请的函数就不可以(如Stack),必须自己写。
class Time
{
public:
Time()
{
_hour = 1;
_minute = 1;
_second = 1;
}
Time& operator=(const Time& t)
{
if (this != &t)
{
_hour = t._hour;
_minute = t._minute;
_second = t._second;
}
return *this;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定义类型
Time _t;
};
int main()
{
Date d1;
Date d2;
d1 = d2;
return 0;
}
2.3前置++和后置++重载
前置++:返回+1之后的结果
后置++:返回+1之前的结果
前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载。
C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器
自动传递。
注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存
一份,然后给this + 1(存在额外空间的使用)。
故更推荐使用前置++。