1. 程式人生 > >類和動態記憶體分配(2)

類和動態記憶體分配(2)

·## 改進後的StringBad類:

//stringbad.h
#include <iostream>
#ifndef STRINGBAD_H_
#define STRINGBAD_H_
class StringBad 
{
private :
	char *str;
	int len;
	static int num_strings;
	static const int CINLIM = 80;//cin的長度限制
public:
	//建構函式和其他方法
	StringBad();
	StringBad(const char * s);
	StringBad(const StringBad &
s); ~StringBad(); int length()const { return len; } //過載方法 StringBad& operator=(const StringBad&s); StringBad& operator=(const char*s); char& operator[](int i); const char& operator[](int i)const; //友元過載方法 friend bool operator<(const StringBad &s1, const StringBad &
s2); friend bool operator>(const StringBad &s1, const StringBad &s2); friend bool operator==(const StringBad &s1, const StringBad &s2); friend std::istream &operator>>(std::istream &is, StringBad &st); friend std::ostream &operator<<(std::ostream &
os, const StringBad &st); //靜態成員方法 static int HowMany(); }; #endif
//stringbad.cpp
#include "stringbad.h"

//初始化靜態成員變數
int StringBad::num_strings = 0;

StringBad::StringBad()
{
	len = 0;
	str = new char[1];
	str[0] = '\0';
	num_strings++;
}

StringBad::StringBad(const char * s)
{
	len = std::strlen(s);
	str = new char[len + 1];
	std::strcpy(str, s);
	num_strings++;
}

StringBad::StringBad(const StringBad & s)
{
	num_strings++;
	len = s.len ;
	str = new char[len + 1];
	std::strcpy(str, s.str);
}

StringBad::~StringBad()
{
	num_strings--;
	if (NULL != str)
	{
		delete[]str;
	}		
}

StringBad & StringBad::operator=(const StringBad & s)
{
	if (this == &s)
		return *this;

	delete[]str;//清空原來的字串
	len = s.len;
	str = new char[len + 1];
	std::strcpy(str, s.str);
	return *this;
}
StringBad & StringBad::operator=(const char * s)
{
	delete[]str;//清空原來的字串
	len = std::strlen(s);
	str = new char[len + 1];
	std::strcpy(str, s);
	return *this;
}
//適用於 StringBad 的[]
char & StringBad::operator[](int i)
{
	if (i < len)
		return str[i];
	else
		return str[len];
}
//適用於 const StringBad[]
const char & StringBad::operator[](int i) const
{
	if (i < len)
		return str[i];
	else
		return str[len];
}



bool operator<(const StringBad & s1, const StringBad & s2)
{
	return (std::strcmp(s1.str, s2.str) < 0);
}

bool operator>(const StringBad & s1, const StringBad & s2)
{
	return s2<s1;
}

bool operator==(const StringBad & s1, const StringBad & s2)
{
	return (std::strcmp(s1.str, s2.str) == 0);
}

std::istream & operator >> (std::istream & is,StringBad & st)
{
	char temp[StringBad::CINLIM];
	is.get(temp, StringBad::CINLIM);
	if (is)
		st = temp;
	while (is&&is.get() != '\n')
		continue;
	return is;
}

std::ostream & operator<<(std::ostream & os, const StringBad & st)
{
	os << st.str;
	return os;
}

//靜態成員函式
int StringBad::HowMany()
{
	return num_strings;
}
//main.cpp
#include"stringbad.h"
using namespace std;
void main()
{
	StringBad  phl = "uuuu";
	StringBad  lll = "uuuu";
	cout << phl.length() << endl;
	cout << phl[1] << endl;
	cout << (phl > lll) << endl;
	cout << (phl < lll) << endl;
	cout << (phl == lll) << endl;
	
	cin >> phl;
	cout << phl << endl;
	cout<<("uuuu" == phl) << endl;

	int count = StringBad::HowMany();
	cout << count << endl;
}

靜態成員函式:

  • 宣告是必須包含關鍵字static,函式定義不能包含關鍵字static。

  • 不能通過物件呼叫靜態成員函式,如果靜態成員成員函式是在公有部分宣告的,則可以使用類名和作用域解析符來呼叫。
    int count = StringBad::HowMany();

  • 靜態成員函式甚至不能使用this指標。由於靜態成員函式不和特定的物件相關聯,因此只能使用靜態資料成員,即只能訪問靜態成員變數。

在建構函式中使用new的注意事項:

  • 在建構函式中使用new初始化物件的指標成員變數,則應該在解構函式中使用delete
  • new和delete必須相容,new對應delete,new[]對應delete[]
  • 如果有多個建構函式,則必須使用相同方式的new,要麼帶中括號,要麼不帶。因為只有一個解構函式,所有的建構函式都必須和他相容。可以在一個建構函式中使用new初始化指標,在另一個建構函式中將指標初始化為空(0或者c++11的nullptr,這是因為delete/delete[]可以用於空指標
  • 應當使用複製建構函式,通過深度複製實現將一個物件初始化另一個物件,即複製建構函式必須分配足夠的空間儲存複製的資料,並複製資料,而不僅僅是資料的地址
StringBad & StringBad::operator=(const StringBad & s)
{
   	if (this == &s)
   		return *this;
   	delete[]str;//清空原來的字串
   	len = s.len;
   	str = new char[len + 1];
   	std::strcpy(str, s.str);
   	return *this;
}
  • 應當定義賦值運算子,通過深度複製將一個物件複製給另一個物件
StringBad & StringBad::operator=(const char * s)
{
   delete[]str;//清空原來的字串
   len = std::strlen(s);
   str = new char[len + 1];
   std::strcpy(str, s);
   return *this;
}

關於返回物件:

  • 返回指向const物件的引用
    使用const物件的引用的目的在於提高效率, 直接返回物件將呼叫複製建構函式,而返回引用不會但是這種方式也存在一些限制。
    適用範圍:函式返回(物件作為引數,或者通過呼叫物件的方法)傳遞給他的物件。 比如:
    const Vector &Max(const Vector&v1,const Vector&v2);
  • 返回指向非const物件的引用
    兩種情形:過載賦值運算子和過載與cout一起用的<<,前者旨在提高效率,後者是必須這樣做。
    operator=()的返回值用於連續賦值:
    String s1(“qwerty”);
    String s2,s3;
    s3=s2=s1;
    operator<<()的返回值用於串拼輸出:
    String s1( hahaha);
    cout<<s1<<“is coming”<<endl;
    其中operator<<(cout,s1)的返回值成為一個用於顯示字串"is comming"的物件。返回型別必須是ostrean&,而不能僅僅是ostream。如果返回型別ostream,將要求呼叫ostream類的複製建構函式,而ostream沒有公有的複製建構函式。
  • 返回物件
    如果返回的物件是被呼叫函式內的定義的區域性變數,則不應該按引用的方式返回,因為在被呼叫函式執行完畢時,區域性物件將呼叫其解構函式。這種情況應該返回物件而不是引用。通常被過載的算術運算子使用返回物件的方式
    Vector Vector::operator+(const Vector &b)const
    {
    return Vector(x+b.x,y+x.y);
    }
    這樣存在呼叫複製建構函式來建立被返回物件的時間開銷,是不可避免的。
  • 返回const物件
    一般不常用

關於使用指向物件的指標

  • 首先,使用new為整個物件分配記憶體,即儲存字串地址的str指標和len分配記憶體(並沒有給num_string成員分配記憶體,因為它是靜態成員,獨立於物件被儲存)。建立物件需要呼叫建構函式,後者分配用於儲存字串的記憶體,並將字串的地址賦值給str。
    //使用new初始化物件:
    String *favorite =new String(saying[choice]);
    
  • 然後,當程式不再需要該物件時,使用delete釋放它。物件是單個的,因此,使用不帶中括號的delete。與前面相同,這將釋放用於儲存str指標和len成員的空間,並不釋放str指向的記憶體,而該任務由解構函式來完成。
     delete favorite;
    

指標和物件小結:

  • 使用常規表示法來宣告指向物件的指標:
    String *glamour;

  • 使用將指標初始化為指向已有的物件:
    String*first=&saying[0];

  • 使用new來初始化指標,這將建立一個新的物件:
    String *favorite = new String(phl);

  • 對類使用new將呼叫建構函式來初始化新建立的物件:
    String *sleep=new String;
    String *glop=new String(“hahaha”);
    String *favorite = new String(phl);

  • 可以使用->運算子通過指標訪問類的方法:
    favorite->length();

  • 可以對物件使用指標應用解除引用運算子(*)來獲取物件
    (*favorite).length();