這個在面試或筆試的時候常問到或考到。 已知類String的原型為: class String { public: String(const char *str = NULL);// 普通構造函數 String(const String &other); // 拷貝構造函數 ~ String(void); // 析構函數 String & operate =(const String &other);// 賦值函數 private: char *m_data;// 用於保存字符串 }; 請編寫String的上述4個函數。 //普通構造函數 String::String(const char *str) { if(str==NULL) { m_data = new char[1]; // 得分點:對空字符串自動申請存放結束標志'\0'的//加分點:對m_data加NULL 判斷 *m_data = '\0'; } else { int length = strlen(str); m_data = new char[length+1]; // 若能加 NULL 判斷則更好 strcpy(m_data, str); } } // String的析構函數 String::~String(void) { delete [] m_data; // 或delete m_data; } //拷貝構造函數 String::String(const String &other) // 得分點:輸入參數為const型 { int length = strlen(other.m_data); m_data = new char[length+1]; //加分點:對m_data加NULL 判斷 strcpy(m_data, other.m_data); } //賦值函數 String & String::operate =(const String &other) // 得分點:輸入參數為const 型 { if(this == &other) //得分點:檢查自賦值 return *this; delete [] m_data; //得分點:釋放原有的內存資源 int length = strlen( other.m_data ); m_data = new char[length+1]; //加分點:對m_data加NULL 判斷 strcpy( m_data, other.m_data ); return *this; //得分點:返回本對象的引用 } 剖析: 能夠准確無誤地編寫出String類的構造函數、拷貝構造函數、賦值函數和析構函數的面試者至少已經具備了C++基本功的60%以上! 在這個類中包括了指針類成員變量m_data,當類中包括指針類成員變量時,一定要重載其拷貝構造函數、賦值函數和析構函數, 這既是對C++程序員的基本要求,也是《Effective C++》中特別強調的條款。 仔細學習這個類,特別注意加注釋的得分點和加分點的意義,這樣就具備了60%以上的C++基本功!
#include<iostream>
using namespace std;
class String
{
friend ostream& operator<<(ostream& out,const String& str) //輸出操作符重載
{
return str.Print(out);
}
public:
String(const char *str = 0);// 普通構造函數
String(const String &other); // 拷貝構造函數
~String(void) { delete [] data_; }// 析構函數
String& operator=(const String &other);// 賦值函數
char* data(void) const { return data_; }
private:
ostream& Print(ostream& out) const;
char *data_; // 用於保存字符串
};
//賦值操作符首先要注意是不是自己賦給自己,如果是這樣的話什么也不做,把自己返回即可。
//其次就是別人賦值給自己,這時首先要自己把原來的值扔到,根據別人的大小開辟一塊空間
//准備盛放別人的內容,最后不要忘了返回對自己的引用。
String& String::operator =(const String& other)
{
if(this!=&other)
{
delete [] data_;
size_t length=strlen(other.data());
data_=new char[length+1];
strcpy_s(data_,length+1,other.data());
}
return *this;
}
//復制構造函數總是發生在構造階段,所以此時成員data_還沒有空間可以使用,應該先根據別
//人空間的大小開辟好空間,然后在把別人的內容拷貝進來。
String::String(const String &other)
{
size_t length=strlen(other.data());
data_=new char[length+1];
strcpy_s(data_,length+1,other.data());
}
//由於輸出操作符通常寫成類的友元函數,這樣就可以寫類似cout<<s;如果不是這樣使用起來就會
//很奇怪,比如可能是s.print()之類,無法像cout<<s<<s1<<endl;那樣和標准庫完美結合,甚至如果
//你寫了一個ostream& operator<<(ostream& out,const String& str)忘了加上友元聲明,編譯器
//會認為你是重載了一元移位操作符<<,而且參數還加多了。
//輸出操作符的經典寫法就像本文這樣,另加一個Print成員函數來完成干活的功能讓<<來調用,之所
//以返回ostream& 也是和C++語言內建操作符機制保持一致,這樣就可以寫cout<<s<<s1<<endl;而不是
//cout<<s;cout<<s1;cout<<endl;
ostream& String::Print(ostream& out) const
{
out<<data_;
return out;
}
//此構造函數可以支持隱式類型轉換比如你可以這樣創建一個String對象 String s("Hello World !");此語句
//就是在調用這個構造函數,另外String s="Hello World !";會被解釋成String s=Sting("Hello World !");先
//根據字符數組構造一個臨時String對象(此對象在這條語句執行完之后就被析構),並緊接着調用String的賦值
//操作符重載函數
String::String(const char *str) // 6分
{
if(str==NULL)
{
data_=new char[1];// 若能加 NULL 判斷則更好
*data_='\0';
}
else
{
size_t length = strlen(str);
data_ = new char[length+1]; // 若能加 NULL 判斷則更好
strcpy_s(data_,length+1, str);
}
}
void main()
{
char* p="Hello World !";
String s(p);
cout<<s<<endl;
String s1("How are you ?");
cout<<s1<<endl;
s1=s;
cout<<s<<endl<<s1<<endl;
s=s=s1;
cout<<s<<endl<<s1<<endl;
}