一、类中的六个默认的成员函数
假如我们现在定义一个类
class Test
{
public:
int GetData()const
{
return m_data;
}
private:
int m_data;};
-
1.构造函数
1、名字和类名相同
2.无返回值。
3.对象实例化时编译器自动调用对应的构造函数
4.构造函数可以重载
Test (int data=0)
{
cout << "Create Test Obj : " << this << endl;
m_data = data;
}
-
2.拷贝构造函数
1.拷贝构造函数是构造函数的一共重载形式
2.拷贝函数的参数只有一个且必须使用引用传参,传值方式会引发无穷递归调用
3.若未显示定义,系统生成默认的拷贝构造函数。默认的拷贝构造函数对象按成员拷贝
(后面的当类成员值为指针的话就不行 )
Test(const Test &t)
{
cout<<"copy success!"<<endl;
m_data=t.m_data;
}
3.赋值运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
3、操作符有一个默认的形参this,限定为第一个形参 .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
Test& operator&=(const &Test t)
{
cout << "Assign :" << this << " = " << &t << endl;
if(this! = &t) //判断是否是给自己赋值
{
m_data=t.m_data;
}
return *this;
}
注意:
1、这里的参数使用引用传递是为了提高效率,加上const是为了不改变原来参数的值
2、有返回值是为了能够实现连等的实现
如 Test t1=t2=t3=t4;
-
4.析构函数
~Test()
{
cout << "Free Test Obj : " << this << endl;
}
二、各函数在调用中的体现
例子:
Test fun(Test& x)
{
int value = x.GetData();
Test tmp(value);
return tmp;
}
void main()
{
Test t(1);
Test t1;
t1 = fun(t);
}
可以看到,fun()函数刚一结束,tmp就被析构了,是靠的无名临时变量把值传递给了t1;
2.不能引用返回局部变量
引用返回确实效率高,但在面对局部变量的话,就不适用了
局部变量在一跳出函数的时候就会被析构,
如果引用返回的话,那么就不会创建临时的无名变量,那么就接收不到值了
3.最优化的代码
编译器是很聪明的,在这里他直接把无名临时对象当成t1了(这样效率高的很)
.三、既然不定义也默认生成成员函数,为什么还要自己去定义呢?
请看下列代码:
当类成员包含指针的时候,我们可以看到明显的报错,这种情况(系统默认的函数已经不起作用了)我们就必须自己去定义函数了。
1.构造函数
所以这时候的构造函数就要change一下了
2.析构函数
假如不自己去定义析构函数的话,就会导致内存泄漏
因为m_data的空间被申请出来了,但是却一直没有被释放,所以就内存泄漏了(只申请,没释放)
(使用vld能够查看到内存泄漏的情况)
3、深拷贝构造函数
我们先来看一个现象:
当我们不自己定义拷贝构造函数的时候:
我们运行到即将退出程序的倒数第二步的时候都是正常的
但最后一步一执行,就立马出现了错误
这是为什么呢?
原因:
在退出主函数的时候,会调用析构函数,但我们的拷贝构造函数在执行的时候进行的是浅拷贝(成员拷贝)
那么析构的时候,S1和S2都析构了一次,这就意味着这给空间被释放了两次,
这就导致了典型的错误: double free
所以我们就要自己定义拷贝构造函数去进行深拷贝
4、深赋值
四、运算符重载的实现
首先要理解:运算符重载的本质就是函数调用
1、重载之加减乘除
提高了程序的可读性,
这些个函数都是成员函数,有this指针的存在,所以写的时候会看起来少一个参数。
2、重载之前加加与后加加
前加加
后加加
这里就能从本质上看出来前加加的效率会更高
3、重载之友元函数
我们先来看一段代码:
明明只是交换一下顺序,a+10可以,10+a就不行了,为什么呢?
这是因为,对象在前面时,可以驱动这个函数(成员函数),但对象在后面的话,就不能够起到驱动的功能
要想实现10+a的话,就必须定义友元函数:
定义友元函数的时候,需要在前面加上一共friend关键字
具体写函数的时候,需要在类的外面去写函数的具体实现。
且友元函数,能访问类的私有(相当于成了这个类的朋友),打破了安全性和封装性
友元不是成员,那么就肯定没有this指针,所以写的时候就比成员函数多了一个参数
4、输入输出流重载
如果直接输出的话,是实现不了的,这时候就要定义输出流的重载
更加了解输出流重载:
当没有返回值的时候
这时候也是可以直接单独输出t的
但是一旦加上了换行,就会发生明显的报错,这是因为什么呢?
当没有返回值的时候,
因为没有返回值,在第一次有"<<"符号的时候,就会去调用一下运算符的重载,
第二次的时候就变成了”无“<<endl,这时候就没有cout在去传参了
当返回值为ostream&的时候,对于cout<<t<<endl一句话,
本质上就是调用了两次输出流重载函数,第一次调用后变为了cout<<endl,会进行第二次调用
5、字符串的运算符重载
1、+=
2、+
其实运算符的重载主要就是为了提高代码的可读性,
重载就是通过operator告诉编译器我们重载的方法的