首页 > 代码库 > 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++面向对象高级编程(三)