人人做人人澡人人爽欧美,国产主播一区二区,久久久精品五月天,羞羞视频在线观看免费

當(dāng)前位置:蘿卜系統(tǒng)下載站 > 技術(shù)開發(fā)教程 > 詳細(xì)頁面

處理C++中動態(tài)內(nèi)存分配引發(fā)的問題

處理C++中動態(tài)內(nèi)存分配引發(fā)的問題

更新時間:2022-07-20 文章作者:未知 信息來源:網(wǎng)絡(luò) 閱讀次數(shù):

  假設(shè)我們要開發(fā)一個String類,它可以方便地處理字符串?dāng)?shù)據(jù)。我們可以在類中聲明一個數(shù)組,考慮到有時候字符串極長,我們可以把數(shù)組大小設(shè)為200,但一般的情況下又不需要這么多的空間,這樣是浪費了內(nèi)存。對了,我們可以使用new操作符,這樣是十分靈活的,但在類中就會出現(xiàn)許多意想不到的問題,本文就是針對這一現(xiàn)象而寫的。現(xiàn)在,我們先來開發(fā)一個Wrong類,從名稱上看出,它是一個不完善的類。的確,我們要刻意地使它出現(xiàn)各種各樣的問題,這樣才好對癥下藥。好了,我們開始吧!

  Wrong.h:

#ifndef WRONG_H_
#define WRONG_H_
class Wrong
{
private:
char * str; //存儲數(shù)據(jù)
int len; //字符串長度

public:
Wrong(const char * s); //構(gòu)造函數(shù)
Wrong(); // 默認(rèn)構(gòu)造函數(shù)
~Wrong(); // 析構(gòu)函數(shù)
friend ostream & operator<<(ostream & os,const Wrong& st);
};
#endif

Wrong.cpp:

#include <iostream>
#include <cstring>
#include "wrong.h"
using namespace std;
Wrong::Wrong(const char * s)
{
len = strlen(s);
str = new char[len + 1];
strcpy(str, s);

}//拷貝數(shù)據(jù)

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

}

Wrong::~Wrong()
{
cout<<"這個字符串將被刪除:"<<str<<'\n';//為了方便觀察結(jié)果,特留此行代碼。
delete [] str;
}

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

test_right.cpp:

#include <iostream>
#include <stdlib.h>
#include "Wrong.h"
using namespace std;
int main()
{
Wrong temp("天極網(wǎng)");
cout<<temp<<'\n';
system("PAUSE");
return 0;
}

  運行結(jié)果:

  天極網(wǎng)

  請按任意鍵繼續(xù). . .

  大家可以看到,以上程序十分正確,而且也是十分有用的。可是,我們不能被表面現(xiàn)象所迷惑!下面,請大家用test_wrong.cpp文件替換test_right.cpp文件進(jìn)行編譯,看看結(jié)果。有的編譯器可能就是根本不能進(jìn)行編譯!

  test_wrong.cpp:

#include <iostream>
#include <stdlib.h>
#include "Wrong.h"
using namespace std;
void show_right(const Wrong&);
void show_wrong(const Wrong);//注意,參數(shù)非引用,而是按值傳遞。
int main()
{
Wrong test1("第一個范例。");
Wrong test2("第二個范例。");
Wrong test3("第三個范例。");
Wrong test4("第四個范例。");
cout<<"下面分別輸入三個范例:\n";
cout<<test1<<endl;
cout<<test2<<endl;
cout<<test3<<endl;
Wrong* wrong1=new Wrong(test1);
cout<<*wrong1<<endl;
delete wrong1;
cout<<test1<<endl;//在Dev-cpp上沒有任何反應(yīng)。
cout<<"使用正確的函數(shù):"<<endl;
show_right(test2);
cout<<test2<<endl;
cout<<"使用錯誤的函數(shù):"<<endl;
show_wrong(test2);
cout<<test2<<endl;//這一段代碼出現(xiàn)嚴(yán)重的錯誤!
Wrong wrong2(test3);
cout<<"wrong2: "<<wrong2<<endl;
Wrong wrong3;
wrong3=test4;
cout<<"wrong3: "<<wrong3<<endl;
cout<<"下面,程序結(jié)束,析構(gòu)函數(shù)將被調(diào)用。"<<endl;
return 0;
}
void show_right(const Wrong& a)
{
cout<<a<<endl;
}
void show_wrong(const Wrong a)
{
cout<<a<<endl;
}


  運行結(jié)果:

  下面分別輸入三個范例:

  第一個范例。
  第二個范例。
  第三個范例。

  第一個范例。

  這個字符串將被刪除:第一個范例。

  使用正確的函數(shù):
  
  第二個范例。
  第二個范例。

  使用錯誤的函數(shù):
  第二個范例。

  這個字符串將被刪除:第二個范例。

  這個字符串將被刪除:?=
  ?=

  wrong2: 第三個范例。
  wrong3: 第四個范例。

  下面,程序結(jié)束,析構(gòu)函數(shù)將被調(diào)用。

  這個字符串將被刪除:第四個范例。

  這個字符串將被刪除:第三個范例。

  這個字符串將被刪除:?=

  這個字符串將被刪除:x =

  這個字符串將被刪除:?=

  這個字符串將被刪除:

  現(xiàn)在,請大家自己試試運行結(jié)果,或許會更加慘不忍睹呢!下面,我為大家一一分析原因。

  首先,大家要知道,C++類有以下這些極為重要的函數(shù):

  一:復(fù)制構(gòu)造函數(shù)。

  二:賦值函數(shù)。

  我們先來講復(fù)制構(gòu)造函數(shù)。什么是復(fù)制構(gòu)造函數(shù)呢?比如,我們可以寫下這樣的代碼:Wrong test1(test2);這是進(jìn)行初始化。我們知道,初始化對象要用構(gòu)造函數(shù)。可這兒呢?按理說,應(yīng)該有聲明為這樣的構(gòu)造函數(shù):Wrong(const Wrong &);可是,我們并沒有定義這個構(gòu)造函數(shù)呀?答案是,C++提供了默認(rèn)的復(fù)制構(gòu)造函數(shù),問題也就出在這兒。

  (1):什么時候會調(diào)用復(fù)制構(gòu)造函數(shù)呢?(以Wrong類為例。)

  在我們提供這樣的代碼:Wrong test1(test2)時,它會被調(diào)用;當(dāng)函數(shù)的參數(shù)列表為按值傳遞,也就是沒有用引用和指針作為類型時,如:void show_wrong(const Wrong),它會被調(diào)用。其實,還有一些情況,但在這兒就不列舉了。

  (2):它是什么樣的函數(shù)。

  它的作用就是把兩個類進(jìn)行復(fù)制。拿Wrong類為例,C++提供的默認(rèn)復(fù)制構(gòu)造函數(shù)是這樣的:

Wrong(const Wrong& a)
{
str=a.str;
len=a.len;
}

  在平時,這樣并不會有任何的問題出現(xiàn),但我們用了new操作符,涉及到了動態(tài)內(nèi)存分配,我們就不得不談?wù)劀\復(fù)制和深復(fù)制了。以上的函數(shù)就是實行的淺復(fù)制,它只是復(fù)制了指針,而并沒有復(fù)制指針指向的數(shù)據(jù),可謂一點兒用也沒有。打個比方吧!就像一個朋友讓你把一個程序通過網(wǎng)絡(luò)發(fā)給他,而你大大咧咧地把快捷方式發(fā)給了他,有什么用處呢?我們來具體談?wù)劊?br>
  假如,A對象中存儲了這樣的字符串:“C++”。它的地址為2000。現(xiàn)在,我們把A對象賦給B對象:Wrong B=A。現(xiàn)在,A和B對象的str指針均指向2000地址。看似可以使用,但如果B對象的析構(gòu)函數(shù)被調(diào)用時,則地址2000處的字符串“C++”已經(jīng)被從內(nèi)存中抹去,而A對象仍然指向地址2000。這時,如果我們寫下這樣的代碼:cout<<A<<endl;或是等待程序結(jié)束,A對象的析構(gòu)函數(shù)被調(diào)用時,A對象的數(shù)據(jù)能否顯示出來呢?只會是亂碼。而且,程序還會這樣做:連續(xù)對地址2000處使用兩次delete操作符,這樣的后果是十分嚴(yán)重的!

  本例中,有這樣的代碼:

Wrong* wrong1=new Wrong(test1);
cout<<*wrong1<<endl;
delete wrong1;

  假設(shè)test1中str指向的地址為2000,而wrong中str指針同樣指向地址2000,我們刪除了2000處的數(shù)據(jù),而test1對象呢?已經(jīng)被破壞了。大家從運行結(jié)果上可以看到,我們使用cout<<test1時,一點反應(yīng)也沒有。而在test1的析構(gòu)函數(shù)被調(diào)用時,顯示是這樣:“這個字符串將被刪除:”。

  再看看這段代碼:

cout<<"使用錯誤的函數(shù):"<<endl;
show_wrong(test2);
cout<<test2<<endl;//這一段代碼出現(xiàn)嚴(yán)重的錯誤!

  show_wrong函數(shù)的參數(shù)列表void show_wrong(const Wrong a)是按值傳遞的,所以,我們相當(dāng)于執(zhí)行了這樣的代碼:Wrong a=test2;函數(shù)執(zhí)行完畢,由于生存周期的緣故,對象a被析構(gòu)函數(shù)刪除,我們馬上就可以看到錯誤的顯示結(jié)果了:這個字符串將被刪除:?=。當(dāng)然,test2也被破壞了。解決的辦法很簡單,當(dāng)然是手工定義一個復(fù)制構(gòu)造函數(shù)嘍!人力可以勝天!

Wrong::Wrong(const Wrong& a)
{
len=a.len;
str=new char(len+1);
strcpy(str,a.str);
}

  我們執(zhí)行的是深復(fù)制。這個函數(shù)的功能是這樣的:假設(shè)對象A中的str指針指向地址2000,內(nèi)容為“I am a C++ Boy!”。我們執(zhí)行代碼Wrong B=A時,我們先開辟出一塊內(nèi)存,假設(shè)為3000。我們用strcpy函數(shù)將地址2000的內(nèi)容拷貝到地址3000中,再將對象B的str指針指向地址3000。這樣,就互不干擾了。

  大家把這個函數(shù)加入程序中,問題就解決了大半,但還沒有完全解決,問題在賦值函數(shù)上。我們的程序中有這樣的段代碼:

Wrong wrong3;
wrong3=test4;

  經(jīng)過我前面的講解,大家應(yīng)該也會對這段代碼進(jìn)行尋根摸底:憑什么可以這樣做:wrong3=test4???原因是,C++為了用戶的方便,提供的這樣的一個操作符重載函數(shù):operator=。所以,我們可以這樣做。大家應(yīng)該猜得到,它同樣是執(zhí)行了淺復(fù)制,出了同樣的毛病。比如,執(zhí)行了這段代碼后,析構(gòu)函數(shù)開始大展神威^_^。由于這些變量是后進(jìn)先出的,所以最后的wrong3變量先被刪除:這個字符串將被刪除:第四個范例。很正常。最后,刪除到test4的時候,問題來了:這個字符串將被刪除:?=。原因我不用贅述了,只是這個賦值函數(shù)怎么寫,還有一點兒學(xué)問呢!大家請看:

  平時,我們可以寫這樣的代碼:x=y=z。(均為整型變量。)而在類對象中,我們同樣要這樣,因為這很方便。而對象A=B=C就是A.operator=(B.operator=(c))。而這個operator=函數(shù)的參數(shù)列表應(yīng)該是:const Wrong& a,所以,大家不難推出,要實現(xiàn)這樣的功能,返回值也要是Wrong&,這樣才能實現(xiàn)A=B=C。我們先來寫寫看:

Wrong& Wrong::operator=(const Wrong& a)
{
delete [] str;//先刪除自身的數(shù)據(jù)
len=a.len;
str=new char[len+1];
strcpy(str,a.str);//此三行為進(jìn)行拷貝
return *this;//返回自身的引用
}

  是不是這樣就行了呢?我們假如寫出了這種代碼:A=A,那么大家看看,豈不是把A對象的數(shù)據(jù)給刪除了嗎?這樣可謂引發(fā)一系列的錯誤。所以,我們還要檢查是否為自身賦值。只比較兩對象的數(shù)據(jù)是不行了,因為兩個對象的數(shù)據(jù)很有可能相同。我們應(yīng)該比較地址。以下是完好的賦值函數(shù):

Wrong& Wrong::operator=(const Wrong& a)
{
if(this==&a)
return *this;
delete [] str;
len=a.len;
str=new char[len+1];
strcpy(str,a.str);
return *this;
}

  把這些代碼加入程序,問題就完全解決,下面是運行結(jié)果:

  下面分別輸入三個范例:

  第一個范例
  第二個范例
  第三個范例

  第一個范例

  這個字符串將被刪除:第一個范例。

  第一個范例

   使用正確的函數(shù):

  第二個范例。

  第二個范例。

   使用錯誤的函數(shù):

  第二個范例。

  這個字符串將被刪除:第二個范例。

  第二個范例。

  wrong2: 第三個范例。
  wrong3: 第四個范例。

  下面,程序結(jié)束,析構(gòu)函數(shù)將被調(diào)用。

  這個字符串將被刪除:第四個范例。
  這個字符串將被刪除:第三個范例。
  這個字符串將被刪除:第四個范例。
  這個字符串將被刪除:第三個范例。
  這個字符串將被刪除:第二個范例。
  這個字符串將被刪除:第一個范例。

  關(guān)于動態(tài)內(nèi)存分配的問題就介紹到這兒,希望大家都能熱愛編程,熱愛C++!

溫馨提示:喜歡本站的話,請收藏一下本站!

本類教程下載

系統(tǒng)下載排行

網(wǎng)站地圖xml | 網(wǎng)站地圖html
主站蜘蛛池模板: 南郑县| 麻城市| 两当县| 南平市| 葫芦岛市| 始兴县| 奉节县| 象州县| 永顺县| 虎林市| 静宁县| 西乌珠穆沁旗| 雅江县| 紫阳县| 始兴县| 南漳县| 民县| 蓬溪县| 四川省| 敦化市| 玉环县| 孙吴县| 西青区| 岗巴县| 卓尼县| 冀州市| 邛崃市| 云安县| 内乡县| 牡丹江市| 宿州市| 普格县| 新宾| 禄劝| 靖宇县| 阳城县| 桐庐县| 常山县| 南召县| 金门县| 公主岭市|