日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

浅谈C++类(1)--概念和构造函数

發布時間:2023/12/4 c/c++ 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅谈C++类(1)--概念和构造函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
迎轉載,但請標明作者 “九天雁翎”,當然,你給出這個帖子的鏈接更好。

類多么重要我就不多說了,只講講學習,因為個人認為類的學習無論從概念的理解還是實際代碼的編寫相對其他C兼容向的代碼都是比較有難度的,?對于以前學C?的人來說這才是真正的新概念和內容,STL其實還比較好理解,不就是一個更大的函數庫和代碼可以使用嘛。雖然vector,string就是類,不過我們卻不需要這樣去理解他們,就可以很好的使用了。

先說明,1,這是非常初級的東西。2,你懂了就不需要看了。3,我寫出來是幫助還不懂得人。4,我自己也還不太懂,所以才寫下來,梳理一下,希望自己能更好的理解,因為我相信一句話,很好的理解一個東西的好方法是把這個東西教給別人。有什么不對的地方,歡迎指出,我非常感謝,還有很多時候,某種方法是不允許的,了解也很重要,但我不想給出錯誤的例子,那樣很容易給出誤導,只講這樣是錯誤的,希望你可以自己輸入去嘗試一下,看看得出的是什么錯誤。

一、概念:就Bjarne Stroustup自己說,來自于Simula的概念(The Design and Evolution of C++),我不懂Simula,所以,還是對我沒有什么幫助,基本上,都說類是具體對象(實例)的抽象,怎么抽象?就是把一個實例的特征拿出來,比如,水果是一個類,蘋果就是一個實例,蘋果有水果的特征。我們只要從蘋果香蕉中把特征抽象出來“class Fruits{ }”;就好了。然后 “Fruits apple”,表示蘋果是一個水果。就像人是一個類的話,我們就都是實例。一下也講不清,不過也可以從另一個角度去理解,就是Bjarne Stroustup自己說的,一個Class其實就是一個用戶定義的新Type,這點上和Struct沒有什么本質上的區別,只是使用上的區別而已。之所以沒有把它直接叫作Type是因為他的一個不定義新名字的原則。

二、使用:我一直覺得比較惱火,光看概念是沒有用的,學習程序,自己編寫代碼是最快的。下面是幾個步驟:

1:最簡單的一個類。

C++中使用任何東西都要先定義吧,類也不例外。用水果舉例,水果的特征最起碼的名字先這1個吧。名字用string表示。

例1.0:

#include <string>
#include <iostream>
using namespace std;
class Fruit?????????????? //定義一個類,名字叫Fruit
{
public:??????????????? //標號,表示這個類成員可以在外部訪問
?string name;??????????
};
int main()
{
?Fruit apple = {"apple"};??//定義一個Fruit類對象apple
?cout<< apple.name<<endl;??//使用apple的成員name
?return 0;
}

在這里說明,以后其他細節我都省略說明了,比如#include,using,cout等等,先去學會吧。我只說類;你會發現其實在這里把class換成struct沒有任何問題,的確,而且換成sturct后"public:" 標號都可以省略,記住,在C++里面,struct與class其實沒有本質的區別,只是stuct默認成員為public而class默認為private。public顧名思義,就是公共的,誰都可以訪問,private自然就是私人的,別人就不能訪問了,你把例1.0的public:標號這行去掉試試。你會得到兩個錯誤,1,不能通過?Fruit apple = {"apple"};形式定義,2,cout<<行不能訪問私有成員。這里class幾乎就和c里面的struct使用沒有區別,包括apple.name點操作符表示使用對象apple里面的一個成員,還有Fruit apple = {"apple"};這樣的定義初始化方法。很好理解吧,不多說了。說點不同的,C++里面class(struct)不僅可以有數據成員,也可以有函數成員。比如,我們希望類Fruit可以自己輸出它的名字,而不是我們從外部訪問成員。

例1.1:

#include <string>
#include <iostream>
using namespace std;
class Fruit?????????????? //定義一個類,名字叫Fruit
{
public:??????????????? //標號,表示這個類成員可以在外部訪問
?string name;??????????? //定義一個name成員??????????
?void print()????????????? //定義一個輸出名字的成員print()
?{
??cout<< name<<endl;
?}
};
int main()
{
?Fruit apple = {"apple"};??//定義一個Fruit類對象apple
?apple.print();??//使用apple的成員print
?return 0;
}

這里你會發現與C的不同,而這看起來一點點地不同,即可以在class(struct)中添加函數成員,讓C++有了面向對象特征,而C只能是結構化編程(這在C剛出來的時候也是先進的代表,不過卻不代表現在的先進編程方法)。還有,你發現定義函數成員和定義普通函數語法是一樣的,使用上和普通成員使用也一樣。再進一步,在C++中有構造函數的概念,先看例子

例1.2:


#include <string>
#include <iostream>
using namespace std;
class Fruit?????????????? //定義一個類,名字叫Fruit
{
public:??????????????? //標號,表示這個類成員可以在外部訪問
?string name;??????????? //定義一個name成員??????????
?void print()????????????? //定義一個輸出名字的成員print()
?{
??cout<< name<<endl;
?}
?Fruit(const string &st)????? //定義一個函數名等于類名的函數成員
?{
??name = st;
?}

};
int main()
{
?Fruit apple = Fruit("apple");??//定義一個Fruit類對象apple
?Fruit orange("orange");
?apple.print();??//使用apple的成員print
?orange.print();????
?return 0;
}

例子1.2里面的函數名等于類名的函數成員就叫作構造函數,在每次你定義一個新對象的時候,程序自動調用,這里,定義了2個對象,一個apple, 一個orange,分別用了2種不同的方法,你會發現構造函數的作用,這里,要說的是,假如你還按以前的方法Fruit apple = {"apple"}定義apple你會編譯失敗,因為有了構造函數了,Fruit apple就定義成功了一個對象,讓apple對象等于{"apple"}的使用是不允許的。對象只能等于對象,所以你可以先用Fruit("apple")構造一個臨時的對象,然后讓apple等于它。orange對象的定義就更好理解了,直接調用構造函數嘛。這里要說的是,你不可以直接Fruit banana了,因為沒有可以用的構造函數,而沒有用構造函數前,你是可以這樣做的。直接Fruit apple,再使apple.name = "apple",是完全可以的。

例1.3:

#include <string>
#include <iostream>
using namespace std;
class Fruit?????????????? //定義一個類,名字叫Fruit
{
public:??????????????? //標號,表示這個類成員可以在外部訪問
?string name;??????????? //定義一個name成員??????????
?void print()????????????? //定義一個輸出名字的成員print()
?{
??cout<< name<<endl;
?}

};
int main()
{
?Fruit apple;??//定義一個Fruit類對象apple
?apple.name ="apple";? //這時候才初始化apple的成員name
?apple.print();??//使用apple的成員print
??
?return 0;
}

而有了構造函數以后就不能這樣了,怎么樣不失去這種靈活性呢?你有兩種辦法。其一是重載一個空的構造函數,記得,構造函數也是一個函數,自然也可以重載羅。你還不知道什么是重載?那先去學這個簡單的東西吧,類比那家伙復雜太多了。

例1.4:

#include <string>
#include <iostream>
using namespace std;
class Fruit?????????????? //定義一個類,名字叫Fruit
{
public:??????????????? //標號,表示這個類成員可以在外部訪問
?string name;??????????? //定義一個name成員??????????
?void print()????????????? //定義一個輸出名字的成員print()
?{
??cout<< name<<endl;
?}
?Fruit(const string &st)
?{
??name = st;
?}
?Fruit(){}??? //重載一個空構造函數
};
int main()
{
?Fruit apple;??//定義一個Fruit類對象apple,這時是允許的了,自動調用第2個構造函數
?apple.name ="apple";? //這時候才初始化apple的成員name
?apple.print();??//使用apple的成員print
??
?return 0;
}

第二種辦法,就是使用構造函數默認實參;

例1.5

#include <string>
#include <iostream>
using namespace std;
class Fruit?????????????? //定義一個類,名字叫Fruit
{
public:??????????????? //標號,表示這個類成員可以在外部訪問
?string name;??????????? //定義一個name成員??????????
?void print()????????????? //定義一個輸出名字的成員print()
?{
??cout<< name<<endl;
?}
?Fruit(const string &st = "banana")
?{
??name = st;
?}
};
int main()
{
?Fruit apple;??//定義一個Fruit類對象apple
?apple.print();
?apple.name ="apple";? //這時候才初始化apple的成員name
?apple.print();??//使用apple的成員print
??
?return 0;
}

這個程序里面,當你直接定義一個無初始化值的apple對象時,你發現,他直接把name表示為banana。也許現在你會問,為什么需要構造函數呢?這里解釋以前留下來的問題。即不推介使用Fruit apple = {"apple"}的原因。因為這樣初始化,你必須要保證成員可以訪問,當name為私有的時候,這樣可就不奏效了,為什么需要私有呢?這就牽涉到類的數據封裝問題,類有不希望別人訪問的成員,以防破壞內部的完整性,也以防誤操作。這點上要講就很復雜了,不多講了。只講操作吧。

例1.6

#include <string>
#include <iostream>
using namespace std;
class Fruit?????????????? //定義一個類,名字叫Fruit
{
?????????????? //沒有標號了,表示這個類成員不可以在外部訪問,class默認為private哦
?string name;??????????? //定義一個name私有成員??????????
public:??
?void print()????????????? //定義一個輸出名字的成員print()
?{
??cout<< name<<endl;
?}
?Fruit(const string &st = "banana")
?{
??name = st;
?}
};
int main()
{
?Fruit banana;??//定義一個Fruit類對象

?banana.print();
//?banana.name ="apple";? //這時候才改變banana的成員name已經是不允許的了
//? 你要定義一個name等于apple的成員必須這樣:
?Fruit apple("apple");
?apple.print();
??
?return 0;
}

要說明的是,構造函數你必須定義成公用的啊,因為你必須要在外部調用啊。現在講講構造函數特有的形式,初始化列表,這點和一般的函數不一樣。

例1.7:

#include <string>
#include <iostream>
using namespace std;
class Fruit?????????????? //定義一個類,名字叫Fruit
{
??string name;??????????? //定義一個name成員??????????
public:??
?void print()????????????? //定義一個輸出名字的成員print()
?{
??cout<< name<<endl;
?}
?Fruit(const string &st = "banana"):name(st){}? //看到不同了嗎?
};
int main()
{
?Fruit banana;??//定義一個Fruit類對象

?banana.print();
??
?return 0;
}

在參數表后,函數實體前,以“:”開頭,列出的一個列表,叫初始化列表,這里初始化列表的作用和以前的例子完全一樣,就是用st初始化name,問題是,為什么要特別定義這個東西呢?C++ Primer的作者Lippman在書里面聲稱這時許多相當有經驗的C++程序員都沒有掌握的一個特性,因為很多時候根本就不需要,用我們以前的形式就夠了但有種情況是例外。在說明前我們為我們的Fruit加個固定新成員,而且定義后不希望再改變了,比如顏色。

例1.8:

#include <string>
#include <iostream>
using namespace std;
class Fruit?????????????? //定義一個類,名字叫Fruit
{
??string name;???? //定義一個name成員??????????
?const string colour;
public:??
?void print()????????????? //定義一個輸出名字的成員print()
?{
??cout<<colour<<" "<<name<<endl;
?}
?Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst){}?
};
int main()
{
?Fruit apple;??//定義一個Fruit類對象apple
?apple.print();
??
?return 0;
}

在這里你把colour的初始化放到{}里面,用以前的那種方法,你會發現編譯錯誤,因為它是const的,而實際上放在{}里面是個計算階段,而放在初始化列表里面就可以,因為初始化列表的使用是在數據定義的時候就自動調用了,因為這個原因,數據的調用順序和初始化列表里面的順序無關,只和數據定義的順序有關,給兩個例子,比如你在上面的例子中把初始化列表改為":colour(name),name(nst)"沒有任何問題,因為在定義colour前面,name?就已經定義了,但是":name(colour),colour(cst)"卻不行,因為在name定義的時候colour還沒有被定義,而且問題的嚴重性在于我可以通過編譯.........太嚴重了,所以在C++ Primer不推薦你使用數據成員初始化另外一個數據,有需要的話,可以":name(cst),colour(cst)",一樣的效果。另外,初始化列表在定義時就自動調用了,所以在構造函數{}之前使用,你可以看看這個例子:

例1.9 :

#include <string>
#include <iostream>
using namespace std;
class Fruit?????????????? //定義一個類,名字叫Fruit
{
?string name;???? //定義一個name成員??????????
?const string colour;
public:??
?void print()????????????? //定義一個輸出名字的成員print()
?{
??cout<<colour<<" "<<name<<endl;
?}
?Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst)
?{
?name +="s";??? //這時name已經等于"apple“了
?}?
};
int main()
{
?Fruit apple("apple","red");??//定義一個Fruit類對象apple
?apple.print();
??
?return 0;
}

最后輸出red apples。先講到這里吧,你明白一點什么是類沒有?像我一樣學了老半天還不明白的,堅持住,多練習,總能明白的。我現在似乎明白的多一點了:)

總結

以上是生活随笔為你收集整理的浅谈C++类(1)--概念和构造函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。