1. 程式人生 > >C++ class with pointer member(s)

C++ class with pointer member(s)

get space 大致 gif 還需要 -a lin 清理 string 類

  • 正如標題所示:這篇復習帶有指針類型成員的class

考慮到會有以下操作,來設計類

1 {
2     String s1();
3     String s2("hello");
4     String s3(s1);
5     cout << s3 << endl;
6     s3 = s2;
7     cout << s3 << endl;
8 }

函數體內第二行和第三行都是構造函數,一個含參數,一個不含參數。第四行創建一個以s1為初值的對象s3,是一個拷貝的動作,需要一個拷貝構造函數,之後會講到;下一行是輸出,需要一個操作符重載。第六行是一個賦值的操作,是一個拷貝的動作,這樣第四行和第六行都是拷貝動作,所以這兩個操作需要的是不同的函數,第六行需要的是拷貝賦值操作。如果,我們自己不寫,編譯器會給出默認的這兩個操作函數,像上個復數的例子,就使用編譯器給的,而這個string的例子,使用默認的就會出現不好的,想象一下,我們現在有個指針對象,指向一個地址,現在又創建一個新的對象,若只是拷貝,把指針拷貝過來,指向了同一個地方去,這並不是真正的拷貝,所以,只要class中含有帶指針的成員,就不要使用默認的拷貝構造函數和拷貝賦值操作。so,string類的大致設計如下:

 1 class String {
 2 public:
 3     String(const char* cstr = 0);//construct func
 4     //if only the calss with pointer pamater(s),we need design two functions as follow
 5     String(const String& str);//copy construct func,the parameter type is itsown type
 6     String& operator=(const String& str);//
copy assign,the parameter is itsown type 7 8 ~String(); 9 char* get_c_str()const { return m_data; } 10 private: 11 char* m_data;//因為字符串長度不定,所以設置成動態的數組-指針 12 };

下面要設計上述幾個函數

  • 構造函數和析構函數
 1 inline 
 2 String::String(const char* cstr = 0)
 3 {
 4     if (cstr) {//有初值
 5         m_data = new char
[strlen(cstr + 1)]; 6 strcpy(m_data, cstr); 7 } 8 else { 9 m_data = new char[1]; 10 *m_data = \0; 11 } 12 } 13 14 inline 15 String::~String() 16 { 17 delete[] m_data;//clean up 18 }

使用上述函數

{
String s1();
    String s2("hello");
    String* p = new String("hello");
    delete p;//離開前,釋放掉
}

前面兩個,離開時會自動釋放掉,也是調用析構函數,所以上述函數作用域內離開時會調用三次析構函數

先前的復數類,不需要清理,因為它們本來就要死亡了,所以不必要,但是,這裏是動態分配內存,如果不釋放的,就是內存泄漏了。so ,Note:如果class中含指針成員,多半要動態分配內存,那麽對象死亡之前,就是析構函數調用時,將動態分配的內存釋放掉

  • 拷貝構造函數和拷貝賦值操作

class with pointer member(s),一定要這樣做,看下面的圖片(來源於侯捷老師的課件)

技術分享圖片

首先,看第一種情況(第二幅圖),使用default copy construct function,對象的data是指針,至於指針中的內容(‘hello’)是不屬於這個指針的,在做copy動作的時候,b也也只是指針,所以兩個指針指向同一塊內容了。雖然a和b 也有相同內容了,但是b中內容,沒有指針指向它了,而此處‘hello‘’所在的內存塊,有兩個指針同時指向它了,將來若改變a,b指向的內容也會被改變。,著就是淺拷貝;與之對應的就是深拷貝,就是我們自己寫的函數要做的操作.下面看看什麽是深拷貝

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

使用:

 String s3(s1);

這裏s3也是新創建出來的對象,就要調用構造函數,先開辟足夠的空間,然後將要拷貝的內容拷貝進來,這就是深拷貝,如果使用編譯器默認的拷貝構造函數,只是把指針拷貝過來

  • 拷貝賦值操作操作

要把右邊的東西賦值給左邊(Note:左右都有東西),正常思路就是先把左邊清空,然後創建出與右邊一樣大的空間,再將右邊內容拷貝給左邊,so,實現如下:

inline 
String& String::operator=(const String& str)
{
    if (this == &str)//self assignment or not
        return *this;
    delete[] m_data;//kill
    m_data = new char[strlen(str.m_data) + 1];
    strcpy(m_data, str.m_data);
    return *this;
}
//使用
String s4=s1;
  • output 函數
    #include <iostream>
    #include "string.h"
    
    using namespace std;
    ostream& operator<<(ostream& os, const String& str)
    {
        os << str.get_c_str();
        return os;
    }
    //使用
    
    String s1("Hello");
    cout<<s1;

    output 函數,必須為全域函數,這樣才能保證是左邊調用“<<”

  • 總結

下面,我們回顧一下String 類的設計,設計一個class,首先,我們考慮class中需要什麽樣的數據,這裏是字符串,我們字符串中會存放很多字符,當然,我們很容易想到使用數組存放,但是,對於設計字符串卻不是一個好的選擇,因為,數組聲明時必須要指定數組大小,所以,我們選擇指針,將來放多大的內容,使用new的方式動態分配大小,在32位系統中,一個指針占4個byte,所以不管字符串多大,字符串這個對象本身就是4byte;Then,考慮設計哪些函數,構造函數,前面講過了,這裏不多講;上面講過了,因為成員是指針,所以需要設計拷貝構造函數、拷貝賦值操作函數,析構函數(Big Three),設計完這三個函數後,再思考還需要設計哪些函數,由於,我們需要輸出字符串,即需要cout,,所以,我們需要取出m_data中的字符,cout是可以接收這種東西的,所以,設計 char* get_c_str() const {return m_data;} ,只是返回,不改變,所以設計成const型的。

下面是完整的String類的設計的頭文件

技術分享圖片
 1 #pragma once
 2 #ifndef __STRING__
 3 #define __STRING__
 4 #include <cstring>
 5 class String {
 6 public:
 7     String(const char* cstr = 0);//construct func
 8     //if only the calss with pointer pamater(s),we need design two functions as follow
 9     String(const String& str);//copy construct func,the parameter type is itsown type
10     String& operator=(const String& str);//copy assign,the parameter is itsown type
11 
12     ~String();
13     char* get_c_str()const { return m_data; }
14 private:
15     char* m_data;//因為字符串長度不定,所以設置成動態的數組-指針
16 };
17 
18 inline 
19 String::String(const char* cstr = 0)
20 {
21     if (cstr) {//有初值
22         m_data = new char[strlen(cstr + 1)];
23         strcpy(m_data, cstr);
24     }
25     else {
26         m_data = new char[1];
27         *m_data = \0;
28     }
29 }
30 
31 inline 
32 String::~String()
33 {
34     delete[] m_data;//clean up
35 }
36 //copy construct fuction
37 inline
38 String::String(const String& str)
39 {
40     m_data = new char[strlen(str.m_data) + 1];
41     strcpy(m_data, str.m_data);
42 }
43 
44 inline 
45 String& String::operator=(const String& str)//String&:& is reference
46 {
47     if (this == &str)//self assignment or not,&str:& is getting address
48         return *this;
49     delete[] m_data;//kill
50     m_data = new char[strlen(str.m_data) + 1];
51     strcpy(m_data, str.m_data);
52     return *this;
53 }
54 #endif
View Code

C++ class with pointer member(s)