C++ 学习笔记 - Week2 【Boolan.com】

Week2 :String class

1 三个重要函数(Big Three)

  含有指针的类,必须要有这三个函数

1.1 拷贝构造函数

  • 拷贝构造函数用于使用已知的类对象来初始化新定义的类对象。
  • 拷贝构造函数的名称必须和类名称一致。
  • 拷贝构造函数的参数必须以引用传递。

1.1.1 函数定义

class String
{
public:
    String(const String& str);

};

inline String&
String::(const String& str)
{
    m_data = new char[ strlen(str.m_data) + 1 ];
    strcpy(m_data, str.m_data);
}

1.1.2 函数的调用(下面三种对象需要调用拷贝构造函数)

  • 对象以值传递的方式传入函数

    int main()
    {
    String s1("Hello");
    String s2(s1);
    }

  • 对象以值传递放方式从函数返回

  • 对象需要通过另外一个对象进行初始化

1.1.3 浅拷贝和深拷贝

  • 浅拷贝:系统自动调用默认拷贝构造函数;
  • 深拷贝:自定义拷贝构造函数并调用;
    注:在对含有指针成员的对象进行拷贝时,必须自定义拷贝构造函数并调用(即深拷贝),使拷贝后的对象指针成员右自己的内存空间,避免发生内存泄漏。如果只是采用默认拷贝构造函数(即浅拷贝),则可能发生两个指针指向同一块内存。

1.2 拷贝赋值函数

1.2.1 函数定义

class String
{
public:
    String& operator = (const String& str);

};

inline String&
String::operator = (const String& str)
{
    if(this == &str)                //检测自我赋值,如果发生自我赋值,则不再执行拷贝赋值
        return *this;

    delete[] m_data;
    m_data = new char[ strlen(str.m_data) + 1 ];
    strcpy(m_data, str.m_data);
    return *this;
}

1.2.2 函数调用

int main()
{
    String s1("Hello");
    String s2 = s1;
}

1.3 析构函数

  • 当对象结束其生命周期时,调用析构函数以释放内存。
  • 一个类函数中只能右一个析构函数,如果未自定义,则系统自动调用默认析构函数。

1.3.1函数定义

class String
{
public:
    ~String();

}

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

1.3.2函数调用

  • 析构函数不需要用户调用,只需要再类中定义,在对象生命周期结束时,系统会自动调用析构函数。

1.4 为什么含有指针的类要用拷贝构造或拷贝赋值?

  如果再含有指针的对象中不使用拷贝构造或拷贝赋值,则新建立的对象会和已存在的对象,同时指向一个内存地址,发生内存泄漏。

1.5 构造函数、析构函数、赋值函数是每个类最基本的的函数。每个类只有一个析构函数和一个赋值函数。但是有很多构造函数(一个为复制构造函数,其他为普通构造函数。对于一个类A,如果不编写上述四个函数,c++编译器将自动为A产生四个默认的函数.

2 Class String的构造函数和析构函数

  • 含有指针的类函数,必须使用动态分配内存,使用动态分配后必须使用析构函数释放内存。

    inline
    String::String(const char* cstr = 0)
    {
    if(cstr) // 判断传入的参数cstr是否为0;
    { // 如果不为0,则创建一个新到对象,其长度为传入的参数的长度+1;
    m_data = new char[strlen(cstr) + 1];
    strcpy(m_data,cstr); // 将传入的参数复制到新创建的对象中;
    }
    else
    {
    m_data = new char[1]; // 如果传入的参数为0,则新创建一个对象,长度为1,并给新创建的对象赋值为结束符‘\0’;
    *m_data = '\0';
    }
    }

    //strlen()函数测量的字符串的长度不包含结束符(\0);
    

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

3 堆、栈与内存管理

3.1 Stack & heap

  • Stack是存在于某作用域(scope)的一块内存空间。例如当你调用函数,函数本身即会形成一个stack用来放置他锁接受的参数和返回的地址。
  • Heap是指操作系统提供的一块global内存空间,程序可动态分配从中获得若干区块。

3.1.1 Static object的生命期

  • 以下列代码为例,c2即是static object,其生命期再作用域结束之后任然存在,直到整个程序结束,程序结束时会自动调用析构函数释放内存。

    class complex{...};
    ...
    {
    static complex c21,2);
    }

3.1.2 global objects的生命期

  • c3即global object,其生命再整个程序结束之后才结束。

    class Complex{...};
    ...
    Complex c3(1,2);

    int main()
    {
    ...
    }

3.1.3 heap objects的生命期

  • p 即是heap object,其生命再被delete之后才结束。

  • 如果在指针后不delete,则会发生内存泄漏。

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

3.1.4 new函数的实际运行步骤

先分配内存(operator mew()),再调用构造函数

3.1.5 delete函数的实际运行步骤

先调用析构函数,再释放内存(operator delete())

3.1.6 动态分配所得的内存块(VC编译器下)

  • 调试模式下

  • 非调试模式下

备注:
【1】new 用于分配一块内存
【2】strlen(str); 测量字符串str的长度
【3】strcpy(str1,str2); 将字符串str2的值拷贝到str1中
【4】delete[] str;释放内存
【5】参考:http://blog.csdn.net/lwbeyond/article/details/6202256

posted @ 2018-01-15 08:30  Niicer  阅读(135)  评论(0)    收藏  举报