博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++ 脑筋急转弯
阅读量:7072 次
发布时间:2019-06-28

本文共 2803 字,大约阅读时间需要 9 分钟。

最近重新温习一下C++的基础知识,这里给大家分享一下,独痛苦不如众痛苦。

先贴出一段示例代码如下:

class CTest{public:    CTest(){        this->p = new char(5);    };   ~CTest(){        if (!this->p)        {            return;        }        delete this->p;        this->p = NULL;    }   void mFree(){
    delete this->p;     this->p = NULL;   }private: char *p;}; int main(){
  CTest *CCTest = new CTest();   CTest tCTest = *CCTest   delete CCTest;   return 0; }

博主是在vs2013环境下跑的这段代码,就是这么一段人畜无害的代码,运行的时候竟然崩溃了...,没错是崩溃了。

这里面涉及的是最基本的深拷贝和浅拷贝的知识,tCTest 做为临时变量拷贝了CCTest里面的成员参数,但是tCTest.p 和 CCTest->p指向的是同一片内存。

我认为崩溃原因是这样的:当main函数执行结束的时候,会执行 tCTest 的析构函数,而释放 CCTest 也会执行析构函数,同一个内存释放两次所以崩溃。

但vs是在CCTest释放内存时抛出这样的异常:p指向的内存被临时变量tCTest占用所以不能释放,这个要研究一下C++的内存调度机制,和源码才能确定深层次的原因。

那么怎么避免这个问题?老手们肯定知道啦,重构一下拷贝函数就OK啦:

class CTest{public:    CTest(){        this->p = new char(5);    };        CTest(CTest &t){        this->p = new char(5);        if(t.p){memcpy(this->p, t.p, 5);}    };    ~CTest(){        if (!this->p)        {            return;        }        delete this->p;        this->p = NULL;    }  void mFree(){     delete this->p;     this->p = NULL;   }private:    char *p;};

老鸟们肯定熟的很了:CTest a = b; 和 CTest a(b);效果是一样的。

好,我们继续再延伸一下,增加点新玩法:

class CParant{public:    CParant(const char* pchIn){        this->chOutPut = pchIn;    }    ~CParant(){         printf("exit\n");     };    string  & mGetOutputAddr();private:    string chOutPut;};class CChild : public CParant{public:    CChild(const char* pchIn) :CParant(pchIn){        this->p = new char(5);    };        CChild(const CChild& CCChild) :CParant(CCChild){        this->p = new char(5);        if(CCChild.p) memcpy(this->p, CCChild.p, 5);    };    void mFree(){        delete this->p;        this->p = NULL;    }    ~CChild(){        if (!this->p)        {            return;        }        delete this->p;        this->p = NULL;    }private:    char *p;}; int main(){
  CParant* CCParant = new CCParant("father");   CCParant tCCParant = *CCParant   delete CCParant;   return 0; }

 各位猜一猜这回会不会报错?当然不会啦,你都已经改了拷贝函数,怎么会报错呢?

确实不会报错,不过不是因为我重构了拷贝函数,而是因为根本就没有拷贝 char *p 这个成员变量,父对象拷贝是不会拷贝到子类里面的变量,即使赋值一方是由子类实例化的。上面这个例子虽然不会造成程序崩溃,但是也不是完美的,它会造成内存泄露。至于原因就是因为CCParant 在析构的时候没有调用子类的析构函数,怎么办嘞?解决方法如下:

class CParant{public:    CParant(const char* pchIn){        this->chOutPut = pchIn;    }    virtual ~CParant(){         printf("exit\n");     };    string  & mGetOutputAddr();private:    string chOutPut;};

把父类的析构函数变为虚函数,这样析构的时候就会先去调用子类的析构函数,避免子类成员无法释放带来的内存泄露。至于加一个虚函数有什么影响,我们看看下面两张对比图片:

没有设置虚析构函数时:

 

这是加了virtual 关键字之后的:

 可以看到加了虚析构函数后CCParant 内存中多出了一张虚函数的函数指针表,这里面会存储虚函数实现的指针地址。我们还可以换个方式验证:

int main(){   CCParant t("test");   printf("%d", sizeof(t));      return 0;  }

对比一下修改前后的size大小,看看是否相差了4byte,这篇就说这么多,都是比较基础浅显的东西,下一篇我们要深入挖掘,加大力度。

 

转载于:https://www.cnblogs.com/cnblogs-wangzhipeng/p/8025464.html

你可能感兴趣的文章
Git 处理分支冲突 rebase
查看>>
Java设计模式之工厂模式
查看>>
测试经理能力要求
查看>>
linux修改用户密码的问题
查看>>
组策略应用之一:映射网络驱动器
查看>>
java第四次作业
查看>>
Dynamics CRM 请求服务时报access is denied错误
查看>>
Oracle in与exists语句
查看>>
我的友情链接
查看>>
通过文件句柄获取文件的路径
查看>>
【转】什么是非对称加密、数字签名、数字证书
查看>>
2015-06-30(最新)手机号正则表达式- 校验示例
查看>>
spring-mvc 3.* 多视图解析配置实例 ContentNegotiatingViewResolver
查看>>
09.移动先行之谁主沉浮----控件之轮流轰炸——高级控件
查看>>
Python3 与 C# 扩展之~基础衍生
查看>>
完全数
查看>>
HDU2017多校联合 contest 2
查看>>
unicode和utf-8互转
查看>>
在IIS上部署Analysis Services
查看>>
BZOJ2346:[Baltic 2011]Lamp(最短路)
查看>>