C++實現(xiàn)MyString的示例代碼
MyString的構(gòu)造、析構(gòu)、拷貝構(gòu)造、賦值運算
class String
{
char* str;
public:
String(const char* p = NULL) :str(NULL)
{
if (p != NULL)
{
str = new char[strlen(p) + 1];//strlen()計算至'\0'截至的字符數(shù)
strcpy(str, p);
}
else
{
str = new char[1]; //額外提供一個空間
*str = '\0';
}
}
~String()
{
if (str != NULL)
{
delete[] str;
}
str = NULL;
}
//ostream& operator<<(const String* const this, ostream &out)
ostream& operator<<(ostream& out)const //重載插入操作符
{
if (str != NULL)
{
out << str;
}
return out;
}
String(const String& s):str(NULL)
{
//str = s.str; 淺拷貝 是同一個空間,會造成一個空間釋放兩次
//深拷貝
str = new char[strlen(s.str)+1];
strcpy(str, s.str);
}
String& operator=(const String& s)
{
if(this != &s)
{
delete[]str;
str = new char[strlen(s.str)+1]
strcpy(str,s.str);
}
return *this;
}
};
ostream& operator<<(ostream& out, const String& s)
{
s << out;
//s.operator<<(cout);
//operator<<(&s1,cout);
return out;
}
int main()
{
String s1("123");
s1 << cout;
//s1.operator<<(cout);
//operator<<(&s1,cout);
cout << s1 << endl;
//operator<<(cout, s1);
}
前面之所以對空指針構(gòu)建對象提供一個空間的原因:使其在賦值重載中只有指向堆區(qū)一種情況進行處理

通過此方式進行等號運算符重載,然后調(diào)動拷貝構(gòu)造對s2進行重寫構(gòu)造

輸出流重寫
class String
{
char* str;
public:
String(const char* p = NULL) :str(NULL)
{
if (p != NULL)
{
str = new char[strlen(p) + 1];
strcpy(str, p);
}
else
{
str = new char[1]; //額外提供一個空間
*str = '\0';
}
}
~String()
{
if (str != NULL)
{
delete[] str;
}
str = NULL;
}
//ostream& operator<<(const String* const this, ostream &out)
ostream& operator<<(ostream& out)const //重載插入操作符
{
if (str != NULL)
{
out << str;
}
return out;
}
};
int main()
{
String s1("123");
s1 << cout;
//s1.operator<<(cout);
//operator<<(&s1,cout);
}
在這里通過改寫前的代碼 operator<<(&s1,cout); 不難看出,將cout初始化out,隨后將this.str輸出至out
ostream& operator<<(ostream& out)const
此處只能使用引用,因為cout在ostream類中進行轉(zhuǎn)移,該類將拷貝構(gòu)造函數(shù)定義為保護訪問屬性,無法使用cout初始化out,繼而只能使用引用;同樣若我們不想使用實參去初始化形參,可以將拷貝構(gòu)造函數(shù)定義為私有或保護類型
若希望輸出符合cout << s1 << endl;此種形式,需要再寫一個全局函數(shù)
class String
{
char* str;
public:
String(const char* p = NULL) :str(NULL)
{
if (p != NULL)
{
str = new char[strlen(p) + 1];
strcpy(str, p);
}
else
{
str = new char[1]; //額外提供一個空間
*str = '\0';
}
}
~String()
{
if (str != NULL)
{
delete[] str;
}
str = NULL;
}
//ostream& operator<<(const String* const this, ostream &out)
ostream& operator<<(ostream& out)const //重載插入操作符
{
if (str != NULL)
{
out << str;
}
return out;
}
};
ostream& operator<<(ostream& out, const String& s)
{
s << out;
//s.operator<<(cout);
//operator<<(&s1,cout);
return out;
}
int main()
{
String s1("123");
s1 << cout;
//s1.operator<<(cout);
//operator<<(&s1,cout);
cout << s1 << endl;
//operator<<(cout, s1);
}
通過此種形式進行翻轉(zhuǎn),繼而達到符合 cout << s1 << endl; 的形式
MyString加號運算符重載
int main()
{
String s1("123");
String s2("456");
String s3;
s3 = s1 + s2;
S3 = s1 + "789";
s3 = "789" + s1;
}
分別寫三個加號運算符重載,來對應(yīng)上面的三個情況(類+類、類+字符串、字符串+類)
String operator+(const String& s)const
{
char *p = new char(strlen(this->str) + strlen(s.str) + 1);
strcpy(p, this->str);
strcat(p, s.str);
return String(p);
}
第一個為成員函數(shù),但是存在內(nèi)存泄漏,需要進行下面的步驟

在私有成員變量中,創(chuàng)建一個新的構(gòu)造函數(shù),直接將p給到str,而沒有創(chuàng)建新的空間;并且在加號運算符重載進行修改使其調(diào)用私有的構(gòu)造函數(shù)
private:
String(char*p,int)//兩個參數(shù)與公有構(gòu)造區(qū)分
{
str = p;
}
public:
String operator+(const String& s)const
{
char *p = new char(strlen(this->str) + strlen(s.str) + 1);
strcpy(p, this->str);
strcat(p, s.str);
return String(p,1);
}
這樣就解決了原本內(nèi)存泄漏的問題
接下來完成剩余兩個等號運算符重載
String operator+(const char* s)const
{
char* p = new char(strlen(this->str) + strlen(s) + 1);
strcpy(p, this->str);
strcat(p, s);
return String(p, 1);
//return *this + String(s)
//上面的方式更方便,但是會構(gòu)造兩個臨時對象
}
此處需要寫在類外,并且需要類內(nèi)添加友元函數(shù)
friend String operator+(const char* t, const String s);
String operator+(const char* t, const String s)
{
char* p = new char(strlen(s.str) + strlen(t) + 1);
strcpy(p, s.str);
strcat(p, t);
return String(p, 1);
//return String(p) + s; 與上面同理,并且不需要友元函數(shù)
}
討論一個衍生問題
class String
{
private:
char* str;
public:
String(const char* p = NULL) :str(NULL)
{
if (p != NULL)
{
str = new char[strlen(p) + 1];
strcpy(str, p);
}
else
{
str = new char[1]; //額外提供一個空間
*str = '\0';
}
}
~String()
{
if (str != NULL)
{
delete[] str;
}
str = NULL;
}
String(const String& s)
{
//str = s.str; 淺拷貝 是同一個空間,會造成一個空間釋放兩次
//深拷貝
str = new char[strlen(s.str)];
strcpy(str, s.str);
}
String& operator=(const String& s)
{
if (this != &s)
{
delete[]str;
str = new char[strlen(s.str)];
strcpy(str, s.str);
}
return *this;
}
};
String fun()
{
String s2("456");
return s2;
}
int main()
{
String s1;
s1 = fun();
return 0;
}
討論此程序執(zhí)行的過程總共創(chuàng)建了多少個對象:

主函數(shù)運行首先開辟main函數(shù)棧幀,創(chuàng)建s1對象,默認構(gòu)造只有大小為一的空間存放“\0”;之后執(zhí)行fun()函數(shù),分配fun棧幀,然后創(chuàng)建s2對象,創(chuàng)建一個堆區(qū),str指向堆區(qū)空間;并且將按值返回,需要構(gòu)建一個臨時對象(將亡值);
將亡值概念:表達式過程中所產(chǎn)生的不具有名字的一個實體,叫做將亡值;將亡值的生存期僅在表達式的調(diào)用過程中,表達式調(diào)用結(jié)束,將亡值就會結(jié)束
構(gòu)建臨時對象調(diào)用拷貝構(gòu)造,fun函數(shù)結(jié)束,s2生存期結(jié)束,調(diào)動析構(gòu)函數(shù);首先釋放s2調(diào)用資源,再歸還s2空間;回到主函數(shù),把將亡值賦值給s1調(diào)用賦值語句,接著調(diào)用將亡值的析構(gòu)函數(shù)進行釋放
這個過程中總共創(chuàng)建了三個對象,分別是s1、s2、將亡值對象
那么如果我們對fun以引用進行返回
String& fun()
{
String s2("456");
return s2;
}
int main()
{
String s1;
s1 = fun();
return 0;
}
當(dāng)以引用返回,就不會返回一個s2的備份,從引用底層來看會返回s2的地址;這樣會從一個已死亡對象來獲取數(shù)據(jù),繼而會得到隨機值

隨后介紹的右值拷貝構(gòu)造與右值賦值語句可以解決這個問題
到此這篇關(guān)于C++實現(xiàn)MyString的示例代碼的文章就介紹到這了,更多相關(guān)C++ MyString內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
OpenMP中For Construct對dynamic的調(diào)度方式詳解
在本篇文章當(dāng)中主要給大家介紹 OpenMp for construct 的實現(xiàn)原理,與他相關(guān)的動態(tài)庫函數(shù)分析以及對 dynamic 的調(diào)度方式進行分析,希望對大家有所幫助2023-02-02
C++結(jié)合OpenCV實現(xiàn)RRT算法(路徑規(guī)劃算法)
這篇文章主要介紹了C++結(jié)合OpenCV實現(xiàn)RRT算法,RRT算法整體框架主要分為rand、near、new三點的建立和near與new之間的安全性檢查,需要的朋友可以參考下2022-05-05
解析Linux下的時間函數(shù):設(shè)置以及獲取時間的方法
本篇文章是對Linux下的時間函數(shù):設(shè)置以及獲取時間的方法進行了詳細的分析介紹,需要的朋友參考下2013-05-05
C++中rapidjson組裝map和數(shù)組array的代碼示例
今天小編就為大家分享一篇關(guān)于C++中rapidjson組裝map和數(shù)組array的代碼示例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-04-04

