一、特殊成员函数
特殊:函数自动生成
1.构造
构造函数:类中特殊成员函数,用于做初始化
函数名与类名相同,没有返回值(void都不写)
构造函数可以重载(可以有多个版本)
定义对象时自动调用
2.析构
析构函数:类中的特殊成员函数,用于做清理
函数名与类名相同且前面有~,没有返回值也没有参数
析构函数不能重载(只有一个版本)
对象销毁时自动调用
3.拷贝构造
用一个对象去初始化另一个对象(相当于构造函数的特殊版本)
默认的拷贝构造,实现了简单的拷贝(浅拷贝)
如果需要深拷贝,则需要自己去实现
A( const A & obj )
{
cout<<"拷贝构造"<<endl; //自己去实现深拷贝
this->ptr = new int;
*(this->ptr) = *obj.ptr;
}
4.赋值函数
用一个对象去给对象赋值
默认的赋值函数,实现了简单的拷贝(浅拷贝)
如果需要深拷贝,则需要自己去实现
如果没有赋值函数,编译器会自动生成默认的赋值函数
void operator=(const A &obj)
{
cout<<"赋值函数"<<endl; //自己去实现深拷贝----------
*(this->ptr) = *obj.ptr;
//为什么不new新空间:因为赋值函数的前提是已经定义了一个对象了
//eg:A a1;a1=a;
}
浅拷贝,一个对象析构的时候会释放ptr,a2,a3指向就没了,他们释放的时候就会出现doule free的问题然后中断程序执行
只要类中元素有指针都应该进行深拷贝
eg1.浅拷贝
class A
{
public:
#if 1
A(){
cout<<"构造函数 无参版本"<<endl;
}
A(int x){
cout<<"构造函数 int版本"<<endl;
}
A(const A &obj){
cout<<"拷贝构造"<<endl;
}
void operator=(const A&obj){
cout<<"赋值函数"<<endl;
}
#endif
};
int main(void)
{
A i ; //定义对象 构造函数
//A j(3); //定义对象 构造函数 带参
A k(i); //定义对象 拷贝构造
k = i; //赋值
A x = i; //定义对象 拷贝构造
return 0;
}
eg2.深拷贝
class A
{
private:
int *ptr ;
//int val;
public:
A(int x=0) {
ptr = new int;
*ptr = x;
}
#if 1
A( const A & obj ){
cout<<"拷贝构造"<<endl; //自己去实现深拷贝---------
this->ptr = new int;
*(this->ptr) = *obj.ptr;
}
void operator=(const A &obj){
cout<<"赋值函数"<<endl; //自己去实现深拷贝----------
*(this->ptr) = *obj.ptr;
}
#endif
void set_val(int x) {
*ptr = x;
}
void print_val(void) const {
cout<<"val:"<<*ptr<<endl;
}
~A(){
// cout<<"析构函数:"<<ptr<<endl;
delete ptr;
}
};
int main(void)
{
A a1(78);
A a2(a1); //拷贝构造,用a1的数据初始化a2
a2.print_val();
A a3;
a3 = a1; //赋值函数,用a1的数据给a3赋值
a3.print_val();
a1.set_val(189);
a1.print_val();
a2.print_val();
a3.print_val();
return 0;
}
二、运算符重载
运算符重载:给运算符增加一个新版本,使它能够支持新类型 + 、== 、< 、>
运算符重载一般语法形式
<返回值类型> operator 运算符 (参数)
{
函数体;
return;
}
//本质:函数重载
可以通过两种方案实现
1.通过成员函数实现(对象本身做一个参数)
1.通过成员函数实现
//dest+obj
A operator+(const A &obj)
{
A tmp(this->val + obj.val);
return tmp;
}
eg: a1 + a2 //a1.operator +(a2)
~a1 //a1.operator~();
2.通过友元函数重载(函数在类外,参数都写括号里)
通过友元函数实现
//obj1+obj2
1.在类外写函数:
A operator+(const A &obj1, const A &obj2)
{
A tmp(obj1.val + obj2.val);
return tmp;
}
2.在类中写友元声明
friend A operator+(const A &obj1, const A &obj2);
eg: a1 + a2 //operator + (a1,a2);
~a1 //operator ~ (a1);
3.注意返回值的设计
注意返回值的设计:
由于运算符原本的语法规则,因此重载后,一般需要满足以前的规则。
因此需要特别注意返回值问题。
eg:
返回值要做左值,必须返回引用,否则返回的是副本
原本可以做左值的运算符(返回值需要设置为引用):++(前++) 、= 、+= 、-=
原本不能做左值的运算符(返回值不能设置为引用):+ 、 - 、++(后)
eg1:
int& operator++(){ //前++ 由于前++是可以做左值的,所以需要返回&
return a+1;
}
int main
{
int a;
++a=0; //前++可以做左值,左值的结果为a=0;
}
如果没有加引用是int operator++(){...};
没加引用的话 运算之后,没有东西来接收++a运算的返回值(符号重载本质就是函数重载;),
就只能用一个临时变量来接收,再对其临时变量进行赋值是错的,找不到临时变量,
而且就算修改的也是临时变量,不是本身,所以要加引用,就用引用来接收返回值即修改本身的值
和传参类似,要改值就传引用,传形参只是传了一个副本
eg2:
int & fun(int &x)
{
return x;
}
int mian()
{
int i=5;
fun(i)=100;
cout<<i<<endl;------>i=100
}
fun()加了引用后,fun(i)甚至可以做左值,函数返回值的本质在于i,最后即i=100,这是C++和C的差别
注意事项
A、除关系运算符"."、作用域运算符"::"、sizeof运算符和三目运算符"?:"以外,C++中的所有运算符都可以重载
// 其中“=”和“&” 如果没有实现,编译器会自动生成默认版本
B、重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符
C、运算符重载的实质是函数重载,遵循函数重载的选择原则
D、重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构
//优先级,结合性,操作数个数,左值与右值
E、运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造,重载的功能应当与原有功能相类似,避免没有目的地使用重载运算符
F、重载运算符的函数不能有默认的参数,否则就改变了运算符的参数个数
G、重载的运算符只能是用户自定义类型,否则就不是重载而是改变了现有的C++标准数据类型的运算符的规则
H、运算符重载可以通过成员函数的形式,也可是通过友元函数,非成员非友元的普通函数。
//注意返回值的设计
由于运算符原本的语法规则,因此进行重载后,一般需要保留以前的规则。因此需要注意返回值问题。
//例如 原本的运算符可以做左值,则返回值需要设置为引用类型且不能有const
//例如 前++、 = 、+=、-=、[] //在C++标准下 可以做左值
//例如 原本的运算符只能做右值,则返回值根据需求设计即可
//例如 + - 后++ | &
eg1:+、*
class A
{
private:
int val;
public:
A(int x=0): val(x) {
}
void set_val(int x) {
val = x;
}
void print_val(void) const {
cout<<"val:"<<val<<endl;
}
A operator+(const A &obj)
{
//cout<<"operator+"<<endl;
A tmp(this->val + obj.val);
return tmp;
}
A operator*(const A &obj){
return A(this->val * obj.val);
}
};
int main(void)
{
A a1(34);
a1.print_val();
A a2(63);
a2.print_val();
A a3 = a1+a2; // 通过成员函数实现 a1.operator+(a2)
a3.print_val();
return 0;
}
eg2:前++和后++
class A
{
private:
int val;
public:
A(int x=0): val(x) {
}
void set_val(int x) {
val = x;
}
void print_val(void) const {
cout<<"val:"<<val<<endl;
}
A& operator++(){ //前++ 由于前++是可以做左值的,所以需要返回&
this->val = this->val + 1;
return *this;
}
A operator++(int){ //后++ 由于后++不能做左值,所以不需要返回&
A tmp(this->val);
this->val = this->val + 1;
return tmp;
}
};
int main(void)
{
A a1(34);
a1.print_val();
#if 0
A a2 = ++a1; //a1.operator++();
a1.print_val();
A a3;
++a1=a3; //前++可以做左值,左值的结果为a1----->a1=a3=0
a1.print_val();
a2.print_val();
#endif
A a2 = a1++;
a1.print_val();
a2.print_val();
return 0;
}
eg3:友元:
class A
{
private:
int val;
public:
A(int x = 0) : val(x)
{
}
void set_val(int x)
{
val = x;
}
void print_val(void) const
{
cout << "val:" << val << endl;
}
friend A operator+(const A &obj1, const A &obj2);
};
A operator+(const A &obj1, const A &obj2)
{
//cout << "operator+" << endl;
A tmp(obj1.val + obj2.val);
return tmp;
}
int main(void)
{
A a1(34);
a1.print_val();
A a2(63);
a2.print_val();
A a3 = a1 + a2; // 通过友元函数实现 operator+(a1,a2)
a3.print_val();
a1-a2;
return 0;
}
eg4.cout、cin重载
#include <iostream>
using namespace std;
class A
{
private:
int val;
public:
A(int x=0): val(x) {
}
void set_val(int x) {
val = x;
}
void print_val(void) const {
cout<<"val:"<<val<<endl;
}
A operator+(const A &obj)
{
A tmp(this->val + obj.val);
return tmp;
}
friend ostream &operator<<(ostream &out, const A&obj );
friend istream &operator>>(istream &in, A&obj );
};
//支持cout
ostream &operator<<(ostream &out, const A&obj )
{
out<<obj.val;
return out;
}
//支持cin
istream &operator>>(istream &in, A&obj )
{
cin>>obj.val;
return in;
}
int main(void)
{
A a1(34);
// a1.print_val();
A a2(63);
// a2.print_val();
A a3 = a1+a2; //a1.operator+(a2)
// a3.print_val();
cout<<a1<<","<<a2<<","<<a3<<endl; // 假设采用成员方法实现:cout.operator<<(a3) 无法实现
// 假设采用友元方式实现:operator<<(cout,a3) 可行
cin>>a1;
cout<<"a1_val:"<<a1<<endl;
return 0;
}