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

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

class CTest{
public:
CTest(){
this->p = new char();
};
  ~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();
}; CTest(CTest &t){
this->p = new char();
if(t.p){memcpy(this->p, t.p, );}
};
~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();
}; CChild(const CChild& CCChild) :CParant(CCChild){
this->p = new char();
if(CCChild.p) memcpy(this->p, CCChild.p, );
}; 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,这篇就说这么多,都是比较基础浅显的东西,下一篇我们要深入挖掘,加大力度。

C++ 脑筋急转弯的更多相关文章

  1. 123457123456#0#-----com.tym.NaojingJiZhuanWan--前拼后广--脑筋急转弯

    com.tym.NaojingJiZhuanWan--前拼后广--脑筋急转弯

  2. C#LeetCode刷题-脑筋急转弯

    脑筋急转弯篇 # 题名 刷题 通过率 难度 292 Nim游戏   62.5% 简单 319 灯泡开关   31.8% 中等 777 在LR字符串中交换相邻字符   21.9% 中等

  3. 脑筋急转弯——Google 面试

    1. 村子里有100对夫妻,其中每个丈夫都瞒着自己的妻子偷情...村里的每个妻子都能立即发现除自己丈夫之外的其他男人是否偷情,唯独不知道她自己的丈夫到底有没有偷情.村里的规矩不容忍通奸.任何一个妻子, ...

  4. 内涵段子——脑筋急转弯——spider

    # python 3.7 from urllib.request import Request,urlopen import re,time class Neihan(object): def __i ...

  5. UVA 11525 Permutation ——(线段树,脑筋急转弯)

    只要注意到对于譬如:S1*(k-1)! 因为后面k-1个数字的全排列个数刚好是(k-1)!,那么第一个数字就是没有取过的数字的第(S1+1)个即可.取走这个数字以后这个数字就不能再用了,依次类推即可得 ...

  6. Leetcode题目292.Nim游戏(脑筋急转弯)

    题目描述: 你和你的朋友,两个人一起玩 Nim 游戏:桌子上有一堆石头,每次你们轮流拿掉 1 - 3 块石头. 拿掉最后一块石头的人就是获胜者.你作为先手. 你们是聪明人,每一步都是最优解. 编写一个 ...

  7. 剑指offer-面试题44-数字序列中某一位的数字-脑筋急转弯

    /* 题目: 数字以0123456789101112131415…的格式序列化到一个字符序列中. 在这个序列中,第5位(从0开始计数,即从第0位开始)是5,第13位是1,第19位是4,等等. 请写一个 ...

  8. 关于 Chrome 浏览器中 onresize 事件的 Bug

    我在写插件时用到了 onresize 事件,在反复地测试后发现该事件在 Chrome 及 Opera(内核基本与 Chrome 相同,以下统称 Chrome)浏览器打开时就会执行,这种情况也许不能算作 ...

  9. 白板编程浅谈——Why, What, How

    作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://lucida.me/blog/whiteboard-coding-demystified/ 这篇文章节选 ...

随机推荐

  1. 【JDK1.8】JDK1.8集合源码阅读——ArrayList

    一.前言 在前面几篇,我们已经学习了常见了Map,下面开始阅读实现Collection接口的常见的实现类.在有了之前源码的铺垫之后,我们后面的阅读之路将会变得简单很多,因为很多Collection的结 ...

  2. java微信开发API解析(二)-获取消息和回复消息

    java微信开发API解析(二)-获取消息和回复消息 说明 * 本演示样例依据微信开发文档:http://mp.weixin.qq.com/wiki/home/index.html最新版(4/3/20 ...

  3. Delphi语言怎样对自己定义类进行持久化保存及恢复 (性能远比json/xml高)

    Delphi的RTL自身就带有一套非常好的资源持久化保存(IDE设计窗口时,保存为DFM格式及编译到EXE里面的资源文件)及恢复机制(EXE启动时对窗口资源的载入),那么应不是必需再额外用xml/js ...

  4. 開始:触摸MySQL

    学习MySQL.少不了安装一个MySQL数据库,安装想必非常easy,事实上也是.我下载了一个MySQL5.7.百度了一下安装步骤,就成功安装了.相对于Oracle来说要简单不少. 买了两本书,看了& ...

  5. HTML学习笔记之三(localstorage的使用)

    localstorage的使用 1.获取对象 var localstroage = window.localStorage; 2.存储值 localstroage.setItem('openid',' ...

  6. Linux企业运维人员必备150个命令汇总

    命令 功能说明 线上查询及帮助命令(2个) man 查看命令帮助,命令的词典,更复杂的还有info,但不常用. help 查看Linux内置命令的帮助,比如cd命令. 文件和目录操作命令(18个) l ...

  7. mybatis分页练手

    最近碰到个需求,要做个透明的mybatis分页功能,描述如下:目标:搜索列表的Controller action要和原先保持一样,并且返回的json需要有分页信息,如: @ResponseBody @ ...

  8. git 设置和取消代理

    # 设置ss git config --global http.proxy 'socks5://127.0.0.1:1080' git config --global https.proxy 'soc ...

  9. 记一次诡异的jetty问题

    问题出现 用eclipse开发,用jetty跑某个项目时,如果是jsp页面,会出现以下错误. ------------------------------------------------ java ...

  10. [译文]React v16(新特性)

    [译文]React v16(新特性) 查看原文内容 我们很高兴的宣布React v16.0发布了! 这个版本有很多长期被使用者期待的功能,包括: fragments (返回片段类型) error bo ...