MyString類的實現--基礎中的基礎C語言

MyString 類是學習 C++ 的過程當中一個很重要的例子,涉及到面向對象的封裝、堆內存申請和釋放、函數的重載以及 C++ 的 「Big Three」。本例子重點在於複習和理解上述的 C++ 特性,實現的功能並很少。c++

MyString 類的 Header

MyString 的聲明中包含了一個帶指針的 C++ 類應有的函數,而且包含了一些經常使用的功能。其中終點討論一下用 friend 關鍵字修飾的兩個函數,它們是用來提供 ostream 的輸出的。聲明爲 friend 是爲了使 ostream 的對象(例如 cout)能夠訪問到 MyString 的成員。而這兩個函數不聲明爲內部方法的緣由是咱們通常寫輸入輸出語句都是將流對象寫在前,若是聲明成了成員函數則調用的時候要寫成這樣:編程

MyString str; str.operator<<(cout);

這看起來至關怪異,因此咱們但願寫成 cout << str 這樣。這句實際上就是 cout 對象調用重載的運算符,也能夠寫在實例化 cout 對象的類 ostream 裏,可是 ostream 通常來講咱們並不會去改動這個文件,因此寫了一個全局函數來將 ostream 和 MyString 聯繫起來。函數

class MyString { friend std::ostream &operator<<(std::ostream &os, const MyString &str); friend std::ostream &operator>>(std::ostream &os, MyString &str); public: MyString(const char *cstr = nullptr); // Big Three MyString(const MyString &str); MyString &operator=(const MyString &str); ~MyString(); char *get_c_str() const { return m_data; } // operator reload MyString &operator+(const MyString &str); bool operator==(const MyString &str); size_t length() const; private: char *m_data; };

 

C++ 的構造函數和 Big Three

C++ 的 Big Three 包括拷貝賦值、拷貝構造和析構函數。Big Three 主要針對帶有指針的類,類中的指針通常指向類所管理的一些資源,多是該類本身申請的內存或者建立的某種對象。類維護着這些資源,負責它們的生老病死——資源的建立、使用和銷燬。學習

一個類中若是帶有着指針,那麼必需要本身重寫有拷貝構造和拷貝賦值函數。若是不本身重寫,那麼會採起默認的行爲,即逐位拷貝。採起逐位拷貝的對象內部的指針也被拷貝過去,稱爲淺拷貝。咱們但願拷貝一個對象,其管理的資源也一併拷貝一份,稱爲深拷貝。爲了實現深拷貝,重寫的拷貝賦值、拷貝構造函數中應實現資源複製的行爲。而在對象生命週期結束後,其管理的資源也應該關閉或者銷燬,故也須要重寫析構函數。ui

C++ 的這三個函數緊密地聯繫在了一塊兒,具體體現爲邏輯上若是須要重寫其中任何一個函數,那麼另外兩個函數也應該被重寫this

inline MyString::MyString(const char *cstr = nullptr) { if (cstr) { m_data = new char[strlen(cstr) + 1]; strcpy(m_data, cstr); } else { m_data = new char[1]; *m_data = '\0'; } } inline MyString::~MyString() { delete[] m_data; } inline MyString::MyString(const MyString &str) { m_data = new char[strlen(str.m_data) + 1]; strcpy(m_data, str.m_data); } inline MyString &MyString::operator=(const MyString &str) { if (this == &str) return *this; delete[] m_data; m_data = nullptr; m_data = new char[strlen(str.m_data) + 1]; strcpy(m_data, str.m_data); return *this; }

 

在上面的實現中,有兩個值得注意的地方,一個是在拷貝賦值運算符重載中的自賦值檢查,在拷貝賦值運算符函數中,若是不進行檢查,按照邏輯來講,會釋放被賦值對象的資源,若是是同一個對象的自我賦值,則會產生嚴重的後果:字符串資源被刪除,兩個對象指向被回收了的內存,當訪問這個字符串時會出現不可預料的後果。可見,防護式編程不只要求程序對黑客行爲的輸入的那些很離譜的數據作防護,還要對用戶可能出現的錯誤進行防護。spa

另一個值得注意的地方就是重載的等於操做符,用戶在使用的時候有時可能會對字符串連續賦值,就像使用 cout 連續輸出同樣,這種相似於函數的鏈式調用。實現的時候只要返回對象的引用就能夠了。.net

輸入輸出運算符重載

除了鏈式調用須要返回對象引用的這個主意點外,輸入輸出函數的第二個參數的 const 屬性也應該主意的。具體來講輸出函數的第二個參數不會被改變,咱們應該將它聲明爲 const 的;而輸入函數的第二個對象正是咱們但願改變的對象,但願將數據輸入到這個對象中,它應該是非 const 引用。指針

std::ostream &operator<<(std::ostream &os, const MyString &str) { os << str.m_data; return os; } std::ostream &operator>>(std::ostream &os, MyString &str) { os >> str.m_data; return os; }

 

至此一個很是簡單的 C++ MyString 類已經實現完成了,在 STL 中,string 類的實現更加複雜,包括了正則在內的高級功能。這個例子僅僅爲了學習 C++ 的一些特性,還不夠深刻,但願將來能專門研究一下 string 的實現。code

--------------------- 本文來自 ProJ7-Jeffy 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/u013040821/article/details/80455108?utm_source=copy 

相關文章
相關標籤/搜索