首页 > 代码库 > C++面向对象高级编程(三)

C++面向对象高级编程(三)

版权声明:本文为博主原创文章,未经博主允许不得转载。

接下来的几篇文章,我将回忆一下C++的基础.

C++的由两部分组成 1.C++语言 2.C++标准库 本篇文章主要分享我学习C++语言的笔记.


 

本节主要介绍 Big Three 即析构函数,拷贝构造函数,赋值拷贝函数,前面主要围绕不带指针的class complex本节中主要围绕带指针的String类

前面我说过如果你创建的类不带有指针,那么多半你可以不用写析构函数,但是如果你创建了一个带指针的类,那么你必须重写Big Three

创建一个类

class String
{
public:                                 
   String(const char* cstr=0);                     
   String(const String& str);                    
   String& operator=(const String& str);         
   ~String();                                    
   char* get_c_str() const { return m_data; }
private:
   char* m_data; //由于带有指针 ,所以需要重写析构函数,拷贝构造,赋值拷贝
};

 

一.拷贝构造

如下操作会调用拷贝构造函数

String a{"hello"};

 

它的实现为

inline
String::String(const char* cstr) //拷贝构造
{
   if (cstr) {
      m_data = new char[strlen(cstr)+1];
      strcpy(m_data, cstr);
   }
   else {   
      m_data = new char[1];
      *m_data = http://www.mamicode.com/\0;
   }
}

 

这个大家都应该理解


 

二.拷贝赋值

下面做赋值拷贝操作

String a{"hello"};
String b{"world"};
s1 = s2;

 

若果我们使用编译器自带的赋值拷贝就会发生下面的现象

技术分享

这样操作会产生野指针,  因为a和b同时指向 hello  ,没人指向world

同时如果a和b同时指向同一块内存,如果你删掉a的话  b指向的内存也会被删掉,这可不是我们想要的

 所以我们必须重写赋值拷贝函数,下面是正确的赋值拷贝函数

inline
String& String::operator=(const String& str) //拷贝赋值
{
   if (this == &str) //防止自我赋值  //如果不写这个判断,那么执行带1的时候,就会先杀掉自己,导致错误
      return *this;

   delete[] m_data; //1释放自己内存
   m_data = http://www.mamicode.com/new char[ strlen(str.m_data) + 1 ];//2.创建新的内存
   strcpy(m_data, str.m_data); //3.copy
   return *this;
}

 

赋值拷贝的三个步骤: 1.释放自身内存 2.创建新内存 3.copy

注意上面的红色部分


 三.重写操作符

因为string类是你新创建的,所有cout不识别你自己创建的类,所以你要重写一个<<

#include <iostream>
using namespace std;

ostream& operator<<(ostream& os, const String& str) //如果写成成员函数调用的时候是这样 c1 << cout  是不是很难接受啊
{
   os << str.get_c_str();
   return os;
}

 


 

四.生命周期

下面我来介绍一个内存管理

1.Stack(栈),是存在于某作用域 (scope) 的一一块内存空间 (memory space)。例如当你调用函数,函数本身即 會形成一個 stack 用来放置它所接的参数,以及返回地址

2.Heap, system heap,是指由操作系統提供的 一块 global 內存空間,程序可动态分配 (dynamic allocated) 从某中获得若干区块 (blocks)

例如

String s1{"hello"};//stack
String s2 = new String("world");// 动态分配  heap
static Complex c2(1,2); //static

 

stack 栈 的生命周期在作用域结束之际结束.自动清理

heap 堆 的生命周期在他被调用delete之际结束

static 静态对象 生命周期会一直存在带程序结束之际

global 全局对象 其生命在整个程序结束之后 才结束。你也可以把它视为一种 static object,其作用域 是「整个程序」


 

五.内存管理

先说一下new 和 delete 的调用过程

1. new:先分配 memory, 再調用 ctor 

 Complex* pc = new Complex(1,2);

 

编译器会把它翻译成

void* mem = operator new( sizeof(Complex) ); //分配內存,其内部调用malloc
pc = static_cast<Complex*>(mem); //转型
pc->Complex::Complex(1,2); //构造函数 pc->Complex::Complex(1,2);

 

 

2. delete:先調用 dtor, 再釋放 mxemory 

Complex* pc = new Complex(1,2);
...
delete pc;

 

编译器转化为

Complex::~Complex(pc); // 析构函数
operator delete(pc);   // 释放內存 其内部调用feee()

 

下面是说一下动态分配所得的内存块以VC编译器为例

debug版 complex类内涵两个double型成员变量(实部虚部)

技术分享

每格4字节

是不是感到有些惊讶,在debug版下 我们仅new complex() 到底给我们带来多少的内存呢

头部和尾部(红色部分)的00000041是 cookie 是两个4字节内存  cookie负责标记内存 最后一位的1是代表获得内存     如果最后一位是0回收内存  4*2 = 8bit

灰色部分是VC分期内存是赋予的每块debug内存都会有这块内存 4*8 + 4 = 36bit

绿色部分是我们的conplex       8bit

青色部分是补位部分,因为VC下每一块内存必须是16的倍数

那么我们new一个complex系统应该分配给 (4*2) + 36 + 8 + (4*3 补位) = 64bit

 

那么非debug版

 技术分享

4*2  + 8 = 16;

下面我们来看一下带指针的string类

debug版

技术分享

4+(32+4)+(4*2) = 48

非debug版

技术分享

4+(4*2) + 4 = 16

可见指针占用的内存小一些

总结

1.带有指针的class必须重写Big Three这是一个非常良好的习惯

2.指针更省内存

如有不正确的地方请指正

参照<<侯捷 C++面向对象高级编程>>

C++面向对象高级编程(三)