目录
2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。
3.如果没有显示定义拷贝构造,编译器会自动生成拷贝构造。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
3. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。
1.拷贝构造函数
1.1概念
那在创建对象时,要创建一个与已存在对象一某一样的新对象就要用到构造拷贝。
拷贝构造只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
1.2特征
拷贝构造函数也是特殊的成员函数
1.拷贝构造是构造函数的一个重载形式
2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。
class student
{
public:
student()
{
age = 10;
}
//拷贝构造函数
//student(const student st)这是错误的写法
student(const student& st) //正确的写法
{
age = st.age;
}
void ShowAge()
{
cout << "hello" << endl;
}
public:
int age;
};
int main()
{
student st;
st.ShowAge();
//p->age = 10;
return 0;
}
3.如果没有显示定义拷贝构造,编译器会自动生成拷贝构造。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
4.涉及到资源申请时,一定要写拷贝构造,否则就是浅拷贝。
typedef int STDataType;
struct Stack
{
public:
Stack(size_t capacity = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * capacity);
if (NULL == _a)
{
perror("malloc fail");
return;
}
_size = 0;
_capacity = capacity;
}
//拷贝构造
Stack(const Stack& st)
{
_size = st._size;
_capacity = st._capacity;
_a = (STDataType*)malloc(sizeof(STDataType) * _capacity);
if (NULL == _a)
{
perror("malloc fail");
return;
}
memcpy(_a, st._a, sizeof(STDataType) * _size);
}
bool STEmpty()
{
return _size == 0;
}
void STPush(const STDataType& x)
{
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
STDataType* tmp = (STDataType*)realloc(_a, sizeof(STDataType) * newcapacity);
if (NULL == tmp)
{
perror("realloc fail");
return;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_size] = x;
_size++;
}
~Stack()
{
if (_a)
{
free(_a);
_a = NULL;
_capacity = 0;
_size = 0;
}
}
private:
STDataType* _a;
size_t _size;
size_t _capacity;
};
int main()
{
Stack st;
st.STPush(1);
Stack st1 = st;
return 0;
}
2.赋值运算符重载
2.1运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// bool operator==(Date* this, const Date& d2)左操作数是this指针
bool operator==(const Date& d2)
{
return _year == d2._year;
&& _month == d2._month
&& _day == d2._day;
}
private:
int _year;
int _month;
int _day;
};
2.2赋值运算符重载
1. 赋值运算符重载格式
使用引用传参可以提高效率 (const d&)
使用引用返回可以提高返回的效率,还能支持连续赋值(d&)
检测是否自己给自己赋值
返回*this可以支持连续赋值
class Date
{
public:
Date(int year = 1970, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
//赋值运算符重载
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
private:
int _year;
int _month;
int _day;
};
2. 赋值运算符只能重载成类的成员函数不能重载成全局函数
在没有显示定义赋值运算符重载函数,编译器会自动生成一个,如果在全局范围定义了赋值运算符函数就会和编译器自动生成的函数产生冲突。
Date& operator=(Date& left, const Date& right)
{
if (&left != &right)
{
left._year = right._year;
left._month = right._month;
left._day = right._day;
}
return left;
}
class Date
{
public:
Date(int year = 1970, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
//赋值运算符重载
/*Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}*/
private:
int _year;
int _month;
int _day;
};
以上情况会出现编译失败:error C2801: “operator =”必须是非静态成员。
3. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。
和拷贝构造类似用户没有显式实现时,编译器会生成一个默认赋值运算符重载,同样类似的还有如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。
例如栈类:
typedef int STDataType;
struct Stack
{
public:
Stack(size_t capacity = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * capacity);
if (NULL == _a)
{
perror("malloc fail");
return;
}
_size = 0;
_capacity = capacity;
}
//拷贝构造
Stack(const Stack& st)
{
_size = st._size;
_capacity = st._capacity;
_a = (STDataType*)malloc(sizeof(STDataType) * _capacity);
if (NULL == _a)
{
perror("malloc fail");
return;
}
memcpy(_a, st._a, sizeof(STDataType) * _size);
}
//赋值运算符重载
Stack& operator=(const Stack& st)
{
_size = st._size;
_capacity = st._capacity;
_a = (STDataType*)malloc(sizeof(STDataType) * _capacity);
if (NULL == _a)
{
perror("malloc fail");
}
memcpy(_a, st._a, sizeof(STDataType) * _size);
return *this;
}
bool STEmpty()
{
return _size == 0;
}
void STPush(const STDataType& x)
{
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
STDataType* tmp = (STDataType*)realloc(_a, sizeof(STDataType) * newcapacity);
if (NULL == tmp)
{
perror("realloc fail");
return;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_size] = x;
_size++;
}
~Stack()
{
if (_a)
{
free(_a);
_a = NULL;
_capacity = 0;
_size = 0;
}
}
private:
STDataType* _a;
size_t _size;
size_t _capacity;
};
int main()
{
Stack st;
st.STPush(1);
st.STPush(5);
st.STPush(4);
st.STPush(3);
st.STPush(2);
Stack st1;
st1 = st;
return 0;
}
2.3 前置++和后置++重载
C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递。
class Date
{
public:
Date(int year = 1970, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//前置++重载
Date& operator++()
{
_day += 1;
return *this;
}
//后置++重载
Date operator++(int)
{
Date temp(*this);
_day += 1;
return temp;
}
private:
int _year;
int _month;
int _day;
};
3.const成员
被const修饰的“成员函数”称为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
void Print() const //相当于void Print(const Data* this)
{
cout << "Print()const" << endl;
cout << "year:" << _year << endl;
cout << "month:" << _month << endl;
cout << "day:" << _day << endl << endl;
}
注意:
1. const对象不可以调用非const成员函数
2. 非const对象可以调用const成员函数
3. const成员函数内不可以调用其它的非const成员函数
4. 非const成员函数内可以调用其它的const成员函数