目录
一、拷贝构造函数
二、深拷贝与浅拷贝
三、this指针
四、异常机制
一、拷贝构造函数
????????当我们用一个对象去初始化另外一个对象的时候,参数很明显是我的一个类,里面做的初始化很有可能是多种多样的,因此它的实现应该是和我简单的构造函数不一样的。如果我们的成员里面有指针,这个指针有可能是自己的一个独立空间
? ??class people ?? ?{ ?? ??? ? ?? ??? ?int * a; ?? ??? ?public: ?? ??? ??? ?people():a(new int(1024)){}?? ? ?? ??? ??? ?~people(){delete a;} ?? ?}
????????通过这个类定义的对象去初始化另外一个对象的时候,另外的那个对象需求这个a的地址,如果是简单的值得赋值,那么就会遇到一个问题 -> 两个指针指向了同样的一个空间,那么由people是存在这个析构函数的 , 我在上方的这个对象里面,会析构这个地址,而现在用这个对象创建出来的这个对象也需要析构这个地址,因此这个初始化很明显会存在两种不同的操作: ?? ??? ?????????1 简单的成员赋值 ?? ??????????? ?2 我们需要新开空间 ?? ??? ?因此里面的操作就跟原先的构造函数不一样,所以这种初始化我们就应该要另寻机制 -> 拷贝构造函数
????????声明: ?? ??? ?????????拷贝构造函数编译器在你没有写拷贝构造函数的时候,会默认给你生成一个拷贝构造函数,里面做的事情是逐成员赋值。当我们自己写了自己的拷贝构造函数,那么编译器就不会给你生成了 ?? ??????????? ?拷贝构造函数也是一个构造函数,实际上也是对我们的对象进行初始化的,只是这个初始化的值来源于另外一个对象 ,因此它的参数只有一个,就是本类的一个引用
????????语法: ?? ??? ??? ?类名(类名 &){} ?? ??? ??? ?????????我们在使用的时候注意,由于是一个对象初始化另外一个对象,仅此而已,因此我们是不能将我传进来的这个对象的值改变的,因此我们需要加const ?? ??? ??? ?类名(const 类名 &){} ?? ??? ??? ?????????里面的操作看自己的实现,更多的时候我们是直接赋值,少数的时候我们需要开空间
????????????void func(const char * p) ?? ??? ??? ?{ ?? ??? ??? ??? ?printf("%s\n",p); ?? ??? ??? ??? ?*p = 'j';//p指向的这个空间不可更改 ?? ??? ??? ??? ? ?? ??? ??? ??? ?char * q = (char *)p;//c语言里面是可以做的 ?? ??? ??? ??? ? ?? ??? ??? ?} ?? ??? ??? ? ?? ??? ??? ?int main() ?? ??? ??? ?{ ?? ??? ??? ??? ?char arr[] = "hehe"; ?? ??? ??? ??? ?func(arr); ?? ??? ??? ?}
????????区别于c的const ?c++里面的const就一定是const声明了就不能更改,除非你不声明,因此 const 类名 & 和类名 & ?-> 在c++里面实际上参数不一样 ????????建议不要这么做,初始化就是初始化,没有那么多的不一样的地方,建议写拷贝构造的时候建议加上const
? ? ? ? ? ?void func(const char * p) ?? ??? ??? ?{ ?? ??? ??? ??? ?printf("%s\n",p); ?? ??? ??? ??? ?*p = 'j';//p指向的这个空间不可更改 ?? ??? ??? ??? ? ?? ??? ??? ??? ?char * q = (char *)p;//c语言里面是可以做的 ?? ??? ??? ??? ? ?? ??? ??? ?} ?? ??? ??? ? ?? ??? ??? ?int main() ?? ??? ??? ?{ ?? ??? ??? ??? ?char arr[] = "hehe"; ?? ??? ??? ??? ?func(arr); ?? ??? ??? ?}
?? ??? ??? ?区别于c的const ?c++里面的const就一定是const ?? ??? ??? ?声明了就不能更改,除非你不声明 ?? ??? ??? ? ?? ??? ??? ?因此 const 类名 & 和类名 & ?-> 在c++里面实际上参数不一样 ?? ??? ??? ?建议不要这么做,初始化就是初始化,没有那么多的不一样的地方 ?? ??? ??? ?建议写拷贝构造的时候建议加上const ?? ??? ??? ? ?? ??? ??? ?struct people ?? ??? ??? ?{ ?? ??? ??? ??? ?people(int a = 0,char b = 0):a(a),b(b){} ?? ??? ??? ??? ?people(const people & p) ?? ??? ??? ??? ?{ ?? ??? ??? ??? ??? ?a = p.a; ?? ??? ??? ??? ??? ?b = p.b; ?? ??? ??? ??? ?} ?? ??? ??? ??? ?void show() ?? ??? ??? ??? ?{ ?? ??? ??? ??? ??? ?cout << a << " " <<b << endl; ?? ??? ??? ??? ?} ?? ??? ??? ??? ?private: ?? ??? ??? ??? ??? ?int a; ?? ??? ??? ??? ??? ?char b; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ??? ??? ? ?? ??? ??? ?} ?? ??? ??? ? ?? ??? ??? ?int main() ?? ??? ??? ?{ ?? ??? ??? ??? ?people p(1024,'q'); ?? ??? ??? ??? ?p.show(); ?? ??? ??? ??? ? ?? ??? ??? ??? ?people p1(p); ?? ??? ??? ??? ?p1.show(); ?? ??? ??? ?}
二、深拷贝与浅拷贝
? ? ? ? 1浅拷贝逐成员赋值 ?? ????????????但是有的时候会出现问题,有指针 很有可能这个指针指向的是同一个空间,并且一改 两个人都会改, 一释放就多次释放,这个时候我们就应该让这个指针指向自己应该需要的空间 ?? ??? ?这个时候我们就应该要重新申请另外的空间 -> 这种情况我们叫深拷贝
????????2深拷贝? ????????????????申请的空间,要将原空间里面的内容拷贝过来 ?? ??? ?注意:是完全的复制 ?? ??? ??? ?memcpy ?? ??? ?????????不一定所有的指针都是需要用深拷贝的,具体的事情具体对待,有的时候浅拷贝 有的时候是深拷贝
struct people
{
? ? people(int a = 0,char b = 0):a(a),b(b),ptr(new int(a)){cout << "构造函数" << endl;}
? ? people(const people & p)
? ? {
? ? ? ? a = p.a;
? ? ? ? b = p.b;
? ??
? ? ? ? //ptr = p.ptr;
? ? ? ? //根据深拷贝的原则 我们这里就需要申请新的空间
? ? ? ? ptr = new int;
? ? ? ? *ptr = *(p.ptr);
? ? ? ? cout << "拷贝构造函数" << endl;
? ? }
? ? ~people()
? ? {
? ? ? ? if(ptr != nullptr)
? ? ? ? {
? ? ? ? ? ? cout << ptr << endl;
? ? ? ? ? ? delete ptr;
? ? ? ? ? ? ptr = nullptr;
? ? ? ? }
? ? ? ? cout << "析构函数" << endl;
? ? }
? ? void show()
? ? {
? ? ? ? cout << a << " " << b << " ?" << *ptr << endl;
? ? }
? ? private:
? ? ? ? int a;
? ? ? ? char b; ?
? ? ? ? int * ptr;
};
?
people func()
{
? ? people p(111,'s');//构造函数必然会在这里调用
? ? return p;
}
?
int main()
{
? ? /*
? ? people p(1024,'q');
? ?
? ? people p1(p);
? ?
? ? people p2 = p;
? ?
? ? people p3 = func();//按照道理来说是要调用拷贝构造的 ?但是最后
? ? ? ? ? ? ? ? //只调用了一次构造函数 编译器会在这里优化
? ? ? ? ? ? ? ? //有的时候优化不一定好 ?我需要不优化
? ? ? ? ? ? ? ? //编译的时候后面加上 -fno-elide-constructors
? ? */
? ?
??
? ? people q(1024,'r');//q.ptr == 0x01 -> delete q.ptr ->delete 0x01
? ? ? ? ? ? ? ? ? ? //q.ptr = nullptr
? ? q.show();
? ? people q1(q);//q1.ptr == 0x01 -> delete q1.ptr ->delete 0x01
? ? ? ? ? ? ? ? //当q1结束生命的时候 ?会再一次free 这个时候就多次free一个地方了
? ? ? ? ? ? ? ? //不允许
? ? q1.show();
}
三、this指针
????????有的时候我们需要在类里面使用自己本身,实际上就是对象使用对象自己本身,类里面会封装方法,而这些方法是属于这个对象的,我们在使用这个方法的时候必须要将这个类实例化,而在这些方法里面我们就有可能会使用指针->包括这个对象自己的地址 ?? ??? ?如果像方法一样带我自己的地址进去,好像是可以,但是有一个时候它绝对不行 -> 初始化的时候,按照道理来说我应该是可以去使用自己的,为了解决这个需求,在每一个类里面都会隐藏一个指针 ?-> this
????????this不是在类的 -> 没有实例化的时候实际上就没有实体,没有实体哪来地址,因此这个this一定是属于对象自己本身的
????????this就是对象自己的地址,对象不一样,那么this就不一样,谁调用我this就是谁 ?? ??? ??? ?people p; ?? ??? ??? ?&p -> this,得到这个this我们就可以用这个对象里面的成员了 ?? ??? ??? ?使用方式跟在外面使用指针是一样的 ?? ??? ??? ?this -> 成员 这个this肯定是在类里面用得
class people ?? ??? ??? ?{ ?? ??? ??? ??? ?int a; ?? ??? ??? ??? ?int b; ?? ??? ??? ??? ?public: ?? ??? ??? ??? ??? ?people(int a,int b):a(a) ?? ??? ??? ??? ??? ?{ ?? ??? ??? ??? ??? ??? ?this ->a = a; ?? ??? ??? ??? ??? ??? ?this ->b = b; ?? ??? ??? ??? ??? ?} ?? ??? ??? ??? ??? ?void setdata(int a,int b) ?? ??? ??? ??? ??? ?{ ?? ??? ??? ??? ??? ??? ?this -> a = a; ?? ??? ??? ??? ??? ?} ?? ??? ??? ?}
四、异常机制
????????c++里面错误分为两种 ????????????????1 编译错误(这种问题是最简单的) ????????????????2 运行错误(这种问题就非常让人不好受) ?? ? ????????c++里面为了优化运行错误而有了异常处理机制,所谓的异常,就是在程序的运行过程中,由于系统条件、操作不当等原因引起的程序崩溃,这种情况我们就叫异常
????????常见的异常:除数为0,内存分配失败,你要操作一个没有打开的文件,我们往一个没有读端的管道里面写...... ?? ??? ??? ??? ? ????????异常一旦产生,我们的程序就会崩溃,对我们来说是一种毁灭性的打击。为了提高我们程序的健壮性,加强它的容错能力,我们需要对这些异常,进行处理,防止我们的程序意外终止。在c语言里面我们实际上也经常在干这个事情 ?? ??? ??? ?if(p == NULL)//这个时候实际上就是在做异常处理 ?? ??? ??? ??? ??? ??? ?//发现这个错误我们及时处理就可以了 ????????但是用c语言里面的这一套我们需要在各种时间里面都需要去判断,这样就有点不美好,c++为了继续去完成这一套,设置了异常处理,这个机制由三个部分去组成: ?? ??? ??? ??? ?1 抛出异常 ?? ??? ??? ??? ?2 捕捉这个异常 ?? ??? ??? ??? ?3 处理这个异常
????????c++为了这三个步骤引出了三个关键字:throw try catch ????????????????try:检测这个异常 ????????????????throw:抛出这个异常 ????????????????catch:关联这个异常
? ? ? ? ? ? try ?? ??? ??? ?{ ?? ??? ??? ??? ?//检测异常 实际上还是c语言里面的那一套 ?? ??? ??? ??? ?if(a == 0) ?? ??? ??? ??? ? ?? ??? ??? ??? ? ?? ??? ??? ??? ?throw 异常值;//抛出这个异常 ?? ??? ??? ? ?? ??? ??? ?} ?? ??? ??? ? ?? ??? ??? ?catch(异常值类型1) ?? ??? ??? ?{ ?? ??? ??? ??? ?//这里就可以进行错误处理 ?? ??? ??? ?} ?? ??? ??? ?catch(异常值类型2) ?? ??? ??? ?{ ?? ??? ??? ??? ?//这里就可以进行错误处理 ?? ??? ??? ?} ?? ??? ??? ?catch(...)//其余的类型都往这个地方来 ?? ??? ??? ?{ ?? ??? ??? ??? ? ?? ??? ??? ?} ?? ??? ??? ?...这三个点必须写在最后面,前面没有匹配到,那么就匹配这里
?eg.
//有一个功能去做除法运算
double function(double a,double b)
{
? ? if(b > -0.0000001 && b < 0.0000001)//项目里面判断一个double是否等于0必须这么来
? ? {
? ? ? ? cout << "异常出现了" << endl;
? ? ? ? throw 123;//抛出异常
? ? ? ?
? ? }
? ? else
? ? {
? ? ? ? return a / b;
? ? }
? ?
? ?
}
int main()
{
? ? double a = 250;
? ? double b = 0;
? ? double c = 0;
? ? try
? ? {
? ? ? ? //调用可能出现异常的功能的代码
? ? ? ? //我们就应该让它处于try里面
? ? ? ? c = function(a,b);
? ? }
? ? //catch(int)//由于上方抛出的是异常值 那么我们实际上是可以接收这个值
? ? ? ? ? ? ? ? //直接定义变量 如果你不定义变量相当于不获取这个值 只关联这个类型
? ? catch(int hehe)//这个hehe就可以去获取你抛出的异常值
? ? {
? ? ? ? cout << "你的除数为0 = " << hehe << ?endl;
? ? }
? ? catch(char)
? ? {
? ? ? ? cout << "兄弟,你是what异常" << endl; ?
? ? }
? ? catch(...)
? ? {
? ? ? ? cout << "姐妹们,其它的都在这里" << endl;
? ? ? ?
? ? }
? ? return 0;
}
????????我们发现上述的代码好像就是脱裤子放屁,把他变得复杂了。实际上工程里面异常都是用的这种机制,这种机制最大的优势 -> 错误和错误处理分开进行,异常处理机制是面向对象的一种设计思维,我们以后的程序错误处理操作应该要遵循,实际在项目里面有非常多问题 ->?异常处理一定要做关注,将main函数里面的东西全部包含在try ?貌似可以,实际也可以,但是效率低下,我们的这种机制是为防止错误,不是为了防止正确的 -> 正确的地方该怎么跑就怎么去跑
|