成員變數帶指標的string類構建
首先看測驗案例:
#include "string.h"
#include <iostream>
using namespace std;
int main()
{
String s1("hello");
String s2("world");
String s3(s2);
cout << s3 << endl;
s3 = s1;
cout << s3 << endl;
cout << s2 << endl;
cout << s1 << endl;
}
從以上測驗案例中,我們很容易知道要兩個個建構式多載:
//1.普通建構式
String s1("hello");
//2 拷貝建構式
String s3(s2);
還有兩個運算子多載:
//1.=
s3 = s1;
//2.<<
cout << s1 << endl;
現在來思考如何設計:
成員變數:String物件應該維護一個字符陣列這里,采用char*型別
為什么不采用 char[]呢?因為不知道有多少個字符,使用char*只需要在結尾加一個/0即可,
private:
char* m_data;
建構式宣告部分:
有可能直接傳入字串或者string物件,所以要建構式多載,且原值應該是不變的,所以加上const,
String(const char* cstr=0);
String(const String& str);
構造實作
inline
String::String(const char* cstr)
{
if (cstr) {
m_data = https://www.cnblogs.com/lsxkugou/p/new char[strlen(cstr)+1];
strcpy(m_data, cstr);
}
else {
m_data = new char[1];
*m_data ='\0';
}
}
inline
String::String(const String& str)
{
m_data = https://www.cnblogs.com/lsxkugou/p/new char[ strlen(str.m_data) + 1 ];
strcpy(m_data, str.m_data);
}
思考:
1)為什么在String(const String& str)中,不直接對指標進行復制? 這里要采用深拷貝,也是為什么不用編譯器自動提供的拷貝構造的原因,
2)這里使用了動態記憶體,那解構式是不是也要重寫?不重寫的話char指標沒有被釋放,會不會造成記憶體泄漏?結論是,應該重寫解構式:
inline
String::~String()
{
delete[] m_data;
}
運算子多載部分:
1.=號的設計:
我們用=號的時候是這樣一個場景,s3=s1,所以實際上是直接將s1的資料拷貝到s3上,
回傳值應該是&型別,因為回傳的是一個物件,參考更快,
引數是一個const string& 型別,const的原因是不能改變s1的值,&是為了傳輸更快
String& operator=(const String& str);
實作:
inline
String& String::operator=(const String& str)
{
//自我檢測,是不是 s1=s1
if (this == &str)
return *this;
delete[] m_data;
m_data = https://www.cnblogs.com/lsxkugou/p/new char[ strlen(str.m_data) + 1 ];
strcpy(m_data, str.m_data);
return *this;
}
思考:
? 為什么要delete[] m_data;?
? 因為原本可能左值就已經保存了一個字串的地址了,將其釋放才能重新賦值,不然就會造成記憶體泄漏,
? 為什么必須要自我檢測?不僅僅是更快, 假設是 s1 = s1;這種情況 delete[] m_data;將會將左右值維護得字串全部清空,那這樣strcpy就無法正確復制記憶體了,
2.<<的設計
我們經常使用cout<< string 這樣的模式
所以需要兩個引數,第一個是string型別,第二個是cout物件,也就是ostream型別;
思考:string型別是完全不用變的,所以直接加const,而ostream里的緩沖區是時刻在變的,所以不能加const,
回傳值型別應該是什么?
首先想到的應該是void,因為只需要將string里的字符陣列輸入到ostream流中即可,
但是還有另一種情況,即: cout << s1 << s2.這樣的連續輸出
因此,我們回傳的應該是ostream. 這樣進行流的套接,
最后:且因為這個符號左邊是輸出流物件,右邊是字串物件,所以不能寫在類內部,應該直接設定成全域函式,寫在類內部的話,左值固定為this,這樣就成了 s1<<cout,不符合人的使用邏輯,
因此函式原型為:
ostream& operator<<(ostream& os, const String& str)
下面是全部代碼:
#ifndef __MYSTRING__
#define __MYSTRING__
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;
};
#include <cstring>
inline
String::String(const char* cstr)
{
if (cstr) {
m_data = https://www.cnblogs.com/lsxkugou/p/new char[strlen(cstr)+1];
strcpy(m_data, cstr);
}
else {
m_data = new char[1];
*m_data ='\0';
}
}
inline
String::~String()
{
delete[] m_data;
}
inline
String& String::operator=(const String& str)
{
if (this == &str)
return *this;
delete[] m_data;
m_data = https://www.cnblogs.com/lsxkugou/p/new char[ strlen(str.m_data) + 1 ];
strcpy(m_data, str.m_data);
return *this;
}
inline
String::String(const String& str)
{
m_data = new char[ strlen(str.m_data) + 1 ];
strcpy(m_data, str.m_data);
}
#include
using namespace std;
ostream& operator<<(ostream& os, const String& str)
{
os << str.get_c_str();
return os;
}
#endif
從string里得到的經驗
-
假設類中有指標變數,就意味著要動態分配記憶體 ,因為不動態分配記憶體,執行完構造之后,堆疊上的函式記憶體就會被清空,指標將會指向一個不合法的地址,
-
假設類中有指標變數,則必須重寫解構式去釋放它,既然使用了動態記憶體,那就意味堆中的記憶體被分配出去了,若采用默認的解構式,消滅指標所占記憶體(&x),但是并沒有消滅指標所指向地址所占記憶體(*x),因此,必須在解構式中delete指標,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/233353.html
標籤:C++
