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;
};

ctor和dtor构造函数和析构函数

inline
String::String(const char* cstr = 0) {
    if (cstr) {
        m_data = new char[strlen(cstr)+1];
        strcpy(m_data,sstr);
    } else {
        m_data = new  char[1];
        *m_data = '\0';
    }
}

inline
String::~String() {
    delete[] m_data;
}

class with pointer members must have copy ctor & copy op=

假设存在两个字符串a,b

String a("Hello");
String b("World");

实际上a,b中存放的是一个 pointer "data" ,其指向"Hello"和"World"所在的内存空间
假设运行

b=a;

如果使用default copy ctor或default op=
那么b中的 pointer "data" 实际上只是把指向变成了"Hello"而已
"World"此时变成了空指向,造成了memory leak
这种「浅拷贝」的另一个弊端是,将来对a进行操作时也会影响到b

copy ctor与copy assignment operator

拷贝构造是单操作 把s2的内容作为蓝本复制给s1
而拷贝赋值是两个操作 先把s1清空 建立一个与等号右边s2相同大小的空间 然后再将s2的值作为蓝本复制过去

inline
String& String::operator=(const String& str) {
    if(this == &str)
        return *this;
    //检测是否则我赋值self assignment
    delete[] m_data;
    m_data = new char[strlen(str.m_data) + 1];
    strcpy(m_data, str.m_data);
    return *this;
}

stack and heap

stack是存在于某作用scope(域)的一块memory space(内存空间)。当你调用函数时,函数本身会形成一个stack用来放置它所接收的参数,以及返回地址
在函数本体内声明的任何变量,其所使用的的内存块都取自上述的stack

heap或者所谓的system heap,是指由操作系统提供的一块global内存空间,程序可dynamic allocated(动态分配)从某种获得若干个blocks(区块)

class Complex { ... };
...
{
    Complex c1(1,2);
    //c1所占用的空间来自stack
    Complex* p = new Complex(3);
    //Complex(3)是个临时对象,其占用空间是以new自heap动态分配而得,然后由p指向它
    //离开了括号后,c1的声明就结束了,而Complex(3)需要手动释放掉
}

生命周期

对于stack objects,其生命在作用于结束的时候就结束了,又称为auto object(自动调用析构函数清理)

{
    Complex c1(1,2);
}

对于static local objects,其生命在作用域结束之后仍然存在,直到整个程序结束

{
    Static Complex c2(1,2);
}

对于global objects,其生命周期也是在整个程序结束之后才结束,可以将其视为一种"static"

Complex c3(1,2);
int main() {
    ...
}

对于heap objects,下面p所指的就是heap object,其生命在被delete之后结束
如果不进行delete,作用域结束之后,p所指的heap object仍然存在,但pointer p的生命就结束了,此时就出现了memory leak

{
    Complex* p = new Complex;
    ...
    delete p;
}

new:先分配memory,再调用ctor

Complex* pc = new complex(1,2);
//编译器转化为
Complex *pc;
void* men = operator new(sizeof(Complex)); //分配内存,内部调用了malloc
pc = static_cast<Complex*>(men); //转型
pc->Complex::Complex(1,2); //构造函数

delete:先调用dtor,再释放memory

delete ps;
//编译器转化为
String::~String(ps); //析构函数
operator delete(ps); //释放内存 内部调用了free(ps)

动态分配所得的memory block内存块(VC环境下)

请输入图片描述
Complex结构是两个double,因此占8个空间;灰色是调试模式下产生的(32+4);橘色是cookie(4)
要保证每一块都是16的倍数,因此还要填补一定的单元格(墨绿色) 52->64
关于cookie:见图1,整个block共64大小,十六进制为40H(十六进制下16的倍数,最后的四位都是0)
最后的bit为0或1表示block是“给出去”还是“收回来”,执行创建操作时,对programmer而言,此时是获得了一个block,所以是“1”

动态分配所得的array数组

请输入图片描述
图中的“3”是VC编译器记录数组大小,占4byte一个单元格

为何array new要搭配array delete

String* p = new String[3];
...
delete[] p; //调用三次dtor
//见上图右1,如果使用delete[],编译器会执行相应的次数
String* p = new String[3];
...
delete p; //调用一次dtor
//见上图右1,如果使用delete,只会执行一次dtor,那么余下的两个String单元格就造成了memory leak

注意&:String&这是一个reference,&str这是一个取地址符,注意在前还是在后

static知识的补充

//非静态的环境下:
complex c1,c2,c3;
cout << c1.real();
cout << c2.real();
//在Cpp的角度上看,上述的语句等同于
complex c1,c2,c3;
cout << complex::real(&c1);
cout << complex::real(&c2);
//靠this pointer来判断处理哪一个成员对象
class complex {
    double real() const { return this->re; }
    //此处的this不用显式的写出来,但实际存在
private:
    double re, im;
};

/*如果是静态:
static data单独存在的,独一份的
静态函数是没有this pointer的,只能处理静态数据*/

请输入图片描述
一个static的例子,比如银行利率等值,是不应该设置为non-static的

class Account {
public:
    static double m_rate;
    static void set_rate(const double& x) { m_rate = x;}
};
double Account::m_rate = 8.0;

int main() {
    Account::set_rate(5,0);//通过class name调用
    
    Account a;
    a.set_rate(7.0);//通过object调用
}

singleton的改进

class A {
public:
    static A& getInstance { return a; };
    setup() { ... }

private:
    A();
    A(const A& rhs);
    static A a;
    ...
};

上述例子中,即使没有人调用getInstance,但static的A是始终存在的,造成了浪费

//改进的单例模式Meyers Singleton
class A {
public:
    static A& getInstance { return a; };
    setup() { ... }

private:
    A();
    A(const A& rhs);
    static A a;
    ...
};

A& A::getInstance() {
    static A a;
    return a;
}