C++類和物件學習總結
C++中的類與物件
http://blog.csdn.net/qq_32583189/article/details/52412369C++中的類與物件
C中的類與物件
定義類
建立標頭檔案
類和結構體
訪問控制
作用域解析運算子
實現類成員
預設的內聯方法
建立物件
開發環境:Visual Studio 2010
定義類
通常,C++程式將介面(類定義)放在標頭檔案中,並將實現(類方法的程式碼放在原始碼檔案中),本文章以一個學生的例子來完成程式碼。
學生定義:記錄三門課程(語文,英語,數學)的成績,能夠檢視學生的平均成績
建立標頭檔案
現在建立名為student.h的標頭檔案
//student.h -- Student class interface //version 00 #ifndef STUDENT_H_ #define STUDENT_H_ #include <string> class Student{ //class declaration private: std::string name; int ch; int en; int math; float average; void count(){ average = (ch + en + math+0.0F)/3; } public: Student();//建構函式 ~Student();//解構函式 void setName(std::string name); void setChScore(int score); void setEnScore(int score); void setMathScore(int score); void show(); }; #endif
注意:類定義和結構體定義一樣,要在最後加上;(分號)
類和結構體
關鍵字class指明這些程式碼定義了一個類設計,在這裡class和typename不是同義詞,不能使用typename代替class
類描述看上去很像是包含成員函式以及public和private可見性標籤的結構宣告。實際上,C++對結構進行了擴充套件,使之具有相同的特性。它們之間唯一的區別就是,結構體的預設訪問型別是public,而類為private。C++程式猿通常使用類來實現類的描述,而把結構體作為純粹的資料物件表示。
訪問控制
在C++程式中通常使用訪問控制關鍵字來保證自身屬性的可見性。常見的三個關鍵字為private,public,protected
關鍵字含義
privateC++中的預設訪問許可權,也就是說上述student.h檔案中的private關鍵字可以省略。其含義表示為只有該物件自身可以訪問這些屬性或者方法,被private修飾的屬性和方法對於外部來說不可見
public公有許可權,表示被public修飾的屬性和方法可以被外部程式直接呼叫
protected受保護的許可權,表示這些屬性和方法僅僅能被該類和派生類的內部訪問,也就是說,對於外部世界來說,保護成員的行為與private相同,對於派生類來說,保護成員的行為和public相同
作用域解析運算子
在具體實現一個類之前,我們需要先了解一個運算子:作用域解析運算子(::)。該符號的作用是用來標識一個函式所屬的類,例如當我們輸入Student::average()時。表示我們訪問的average()方法屬於Student類而不是其他的類。
但是在一個Student類的內部我們並不需要使用使用作用域解析運算子就能訪問average()方法,這是因為在類的內部自身所定義的所有方法對自身來說都是可見的。
對於一個方法名稱來說,Student::average()是方法的函式限定名(全稱),而average()方法屬於非限定名(簡稱),當程式對於一個函式來源無異議時(沒有重名方法),可以使用簡稱。
實現類成員
現在我們建立一個C++檔案student.cpp
//student.cpp -- implementing the Student class
//version 00
#include "stdafx.h"
#include "student.h"
Student::Student(){
}
Student::~Student(){
}
void Student::setChScore(int score){
Student::ch = score;
}
void Student::setName(std::string n){
Student::name = n;
}
void Student::setEnScore(int score){
en = score;
}
void Student::setMathScore(int score){
math = score;
}
void Student::show(){
Student::count();
ios_base::fmtflags orig = cout.setf(ios_base::fixed,ios_base::floatfield);
std::streamsize prec = cout.precision(1);
cout<<name<<" 同學的語文成績為"<<ch<<"分,數學成績為"<<math<<"分,英語成績為"<<en<<"分,平均成績"<<average<<"分"<<endl;
cout.setf(orig,ios_base::floatfield);
}
注意這個實現的Student::setChScore(int score)中我們在使用ch引數時使用了作用於解析運算子,其實在類的內部我們可以直接使用簡稱,所以在後續的方法中我們使用了非限定名。
預設的內聯方法
我們在類的定義中宣告並定義了一個count()方法,由於其位於宣告中,編譯器將其編譯為內聯方法。一般內容簡單的方法都可以作為內聯方法。如果我們並不想直接在定義中宣告並定義一個內聯方法,我們也可以在實現時給方法新增inline關鍵字。比如
inline void count(){
average = (ch + en + math+0.0F)/3;
}
但是我們在宣告外定義行內函數時必須注意C++中的一個規則:要求行內函數在每一個使用它的原始檔中均由定義。也就是說我們需要在每一個使用該方法的檔案中定義該方法,很明顯我們並不想這樣做,所以最好的實現就是在宣告中實現定義方法。
建立物件
當我們在使用一個類的例項物件時需要用到一個關鍵字new,這個關鍵字和C語言中的malloc關鍵字類似,都是用來在堆記憶體中為資料分配一段空間。
我們在main方法中舉個例子
//visual studio 2010 -- main program
#include "stdafx.h"
#include "student.h"
int _tmain(int argc, _TCHAR* argv[]){
Student *jack = new Student();
jack->setName("jack");
jack->setChScore(98);
jack->setMathScore(100);
jack->setEnScore(92);
jack->show();
delete jack;
return 0;
}
輸出結果:
jack 同學的語文成績為98分,數學成績為100分,英語成績為92分,平均成績96.7分
請按任意鍵繼續. . .
溫馨提醒:記得養成良好習慣,在物件不用後要使用delete關鍵字銷燬物件。
========
C++類的定義和物件的建立
類和物件是 C++ 的重要特性,它們使得 C++ 成為面向物件的程式語言,可以用來開發中大型專案,本節重點講解類和物件的語法,如果你對它們的概念還不瞭解,請先閱讀《C++類和物件的概念》。
類是建立物件的模板,一個類可以建立多個物件,每個物件都是類型別的一個變數;建立物件的過程也叫類的例項化。每個物件都是類的一個具體例項(Instance),擁有類的成員變數和成員函式。
有些教程將類的成員變數稱為類的屬性(Property),將類的成員函式稱為類的方法(Method)。在面向物件的程式語言中,經常把函式(Function)稱為方法(Method)。
與結構體一樣,類只是一種複雜資料型別的宣告,不佔用記憶體空間。而物件是類這種資料型別的一個變數,或者說是通過類這種資料型別創建出來的一份實實在在的資料,所以佔用記憶體空間。
類的定義
類是使用者自定義的型別,如果程式中要用到類,必須提前說明,或者使用已存在的類(別人寫好的類、標準庫中的類等),C++語法本身並不提供現成的類的名稱、結構和內容。
一個簡單的類的定義:
純文字複製
class Student{
public:
//成員變數
char *name;
int age;
float score;
//成員函式
void say(){
cout<<name<<"的年齡是"<<age<<",成績是"<<score<<endl;
}
};
class是 C++ 中新增的關鍵字,專門用來定義類。Student是類的名稱;類名的首字母一般大寫,以和其他的識別符號區分開。{ }內部是類所包含的成員變數和成員函式,它們統稱為類的成員(Member);由{ }包圍起來的部分有時也稱為類體,和函式體的概念類似。public也是 C++ 的新增關鍵字,它只能用在類的定義中,表示類的成員變數或成員函式具有“公開”的訪問許可權,初學者請先忽略該關鍵字,我們將在《C++類成員的訪問許可權》中講解。
注意在類定義的最後有一個分號;,它是類定義的一部分,表示類定義結束了,不能省略。
整體上講,上面的程式碼建立了一個 Student 類,它包含了 3 個成員變數和 1 個成員函式。
類只是一個模板(Template),編譯後不佔用記憶體空間,所以在定義類時不能對成員變數進行初始化,因為沒有地方儲存資料。只有在建立物件以後才會給成員變數分配記憶體,這個時候就可以賦值了。
類可以理解為一種新的資料型別,該資料型別的名稱是 Student。與 char、int、float 等基本資料型別不同的是,Student 是一種複雜資料型別,可以包含基本型別,而且還有很多基本型別中沒有的特性,以後大家會見到。
建立物件
有了 Student 類後,就可以通過它來建立物件了,例如:
Student liLei; //建立物件
Student是類名,liLei是物件名。這和使用基本型別定義變數的形式類似:
int a; //定義整型變數
從這個角度考慮,我們可以把 Student 看做一種新的資料型別,把 liLei 看做一個變數。
在建立物件時,class 關鍵字可要可不要,但是出於習慣我們通常會省略掉 class 關鍵字,例如:
class Student LiLei; //正確
Student LiLei; //同樣正確
除了建立單個物件,還可以建立物件陣列:
Student allStu[100];
該語句建立了一個 allStu 陣列,它擁有100個元素,每個元素都是 Student 型別的物件。
訪問類的成員
建立物件以後,可以使用點號.來訪問成員變數和成員函式,這和通過結構體變數來訪問它的成員類似,如下所示:
#include <iostream>
using namespace std;
//類通常定義在函式外面
class Student{
public:
//類包含的變數
char *name;
int age;
float score;
//類包含的函式
void say(){
cout<<name<<"的年齡是"<<age<<",成績是"<<score<<endl;
}
};
int main(){
//建立物件
Student stu;
stu.name = "小明";
stu.age = 15;
stu.score = 92.5f;
stu.say();
return 0;
}
執行結果:
小明的年齡是15,成績是92.5
stu 是一個物件,佔用記憶體空間,可以對它的成員變數賦值,也可以讀取它的成員變數。
類通常定義在函式外面,當然也可以定義在函式內部,不過很少這樣使用。
使用物件指標
C語言中經典的指標在 C++ 中仍然廣泛使用,尤其是指向物件的指標,沒有它就不能實現某些功能。
上面程式碼中建立的物件 stu 在棧上分配記憶體,需要使用&獲取它的地址,例如:
Student stu;
Student *pStu = &stu;
pStu 是一個指標,它指向 Student 型別的資料,也就是通過 Student 創建出來的物件。
當然,你也可以在堆上建立物件,這個時候就需要使用前面講到的new關鍵字,例如:
Student *pStu = new Student;
在棧上創建出來的物件都有一個名字,比如 stu,使用指標指向它不是必須的。但是通過 new 創建出來的物件就不一樣了,它在堆上分配記憶體,沒有名字,只能得到一個指向它的指標,所以必須使用一個指標變數來接收這個指標,否則以後再也無法找到這個物件了,更沒有辦法使用它。也就是說,使用 new 在堆上創建出來的物件是匿名的,沒法直接使用,必須要用一個指標指向它,再借助指標來訪問它的成員變數或成員函式。
棧記憶體是程式自動管理的,不能使用 delete 刪除在棧上建立的物件;堆記憶體由程式設計師管理,物件使用完畢後可以通過 delete 刪除。在實際開發中,new 和 delete 往往成對出現,以保證及時刪除不再使用的物件,防止無用記憶體堆積。
棧(Stack)和堆(Heap)是 C/C++ 程式設計師必須要了解的兩個概念,我們已在《C語言和記憶體》專題中進行了深入講解,相信你必將有所頓悟。
有了物件指標後,可以通過箭頭->來訪問物件的成員變數和成員函式,這和通過結構體指標來訪問它的成員類似,請看下面的示例:
pStu -> name = "小明";
pStu -> age = 15;
pStu -> score = 92.5f;
pStu -> say();
下面是一個完整的例子:
#include <iostream>
using namespace std;
class Student{
public:
char *name;
int age;
float score;
void say(){
cout<<name<<"的年齡是"<<age<<",成績是"<<score<<endl;
}
};
int main(){
Student *pStu = new Student;
pStu -> name = "小明";
pStu -> age = 15;
pStu -> score = 92.5f;
pStu -> say();
delete pStu; //刪除物件
return 0;
}
執行結果:
小明的年齡是15,成績是92.5
雖然在一般的程式中無視垃圾記憶體影響不大,但記得 delete 掉不再使用的物件依然是一種良好的程式設計習慣。
總結
本節重點講解了兩種建立物件的方式:一種是在棧上建立,形式和定義普通變數類似;另外一種是在堆上建立,必須要用一個指標指向它,讀者要記得 delete 掉不再使用的物件。
通過物件名字訪問成員使用點號.,通過物件指標訪問成員使用箭頭->,這和結構體非常類似。
========
C++類(Class)總結
http://www.cnblogs.com/xiongxuanwen/p/4290086.html一、C++類的定義
C++中使用關鍵字 class 來定義類, 其基本形式如下:
class 類名
{
public:
//行為或屬性
protected:
//行為或屬性
private:
//行為或屬性
};
示例:
定義一個點(Point)類, 具有以下屬性和方法:
屬性: x座標, y座標
方法: 1.設定x,y的座標值; 2.輸出座標的資訊。
實現程式碼:
class Point
{
public:
void setPoint(int x, int y);
void printPoint();
private:
int xPos;
int yPos;
};
程式碼說明:
上段程式碼中定義了一個名為 Point 的類, 具有兩個私密屬性, int型的xPos和yPos, 分別用來表示x點和y點。
在方法上, setPoint 用來設定屬性, 也就是 xPos 和 yPos 的值; printPoint 用來輸出點的資訊。
1 資料抽象和封裝
抽象是通過特定的例項抽取共同特徵以後形成概念的過程。一個物件是現實世界中一個實體的抽象,一個類是一組物件的抽象。
封裝是將相關的概念組成一個單元,然後通過一個名稱來引用它。面向物件封裝是將資料和基於資料的操作封裝成一個整體物件,對資料的訪問或修改只能通過物件對外提供的介面進行。
2 類定義
幾個重要名詞:
(1) 類名
遵循一般的命名規則; 字母,數字和下劃線組合,不要以數字開頭。
(2) 類成員
類可以沒有成員,也可以定義多個成員。成員可以是資料、函式或類型別名。所有的成員都必須在類的內部宣告。
沒有成員的類是空類,空類也佔用空間。
class People
{
};
sizeof(People) = 1;
(3) 建構函式
建構函式是一個特殊的、與類同名的成員函式,用於給每個資料成員設定適當的初始值。
(4) 成員函式
成員函式必須在類內部宣告,可以在類內部定義,也可以在類外部定義。如果在類內部定義,就預設是行內函數。
3 類定義補充
3.1 可使用類型別名來簡化類
除了定義資料和函式成員之外,類還可以定義自己的區域性型別名字。
使用類型別名有很多好處,它讓複雜的型別名字變得簡單明瞭、易於理解和使用,還有助於程式設計師清楚地知道使用該型別的真實目的。
class People
{
public:
typedef std::string phonenum; //電話號碼型別
phonenum phonePub; //公開號碼
private:
phonenum phonePri;//私人號碼
};
3.2 成員函式可被過載
可以有多個過載成員函式,個數不限。
3.3 行內函數
有三種:
(1)直接在類內部定義。
(2)在類內部宣告,加上inline關鍵字,在類外部定義。
(3)在類內部宣告,在類外部定義,同時加上inline關鍵字。注意:此種情況下,行內函數的定義通常應該放在類定義的同一標頭檔案中,而不是在原始檔中。這是為了保證行內函數的定義在呼叫該函式的每個原始檔中是可見的。
3.4 訪問限制
public,private,protected 為屬性/方法限制的關鍵字。
3.5 類的資料成員中不能使用 auto、extern和register等進行修飾, 也不能在定義時進行初始化
如 int xPos = 0; //錯;
例外:
靜態常量整型(包括char,bool)資料成員可以直接在類的定義體中進行初始化,例如:
static const int ia= 30;
4 類宣告與類定義
4.1 類宣告(declare)
class Screen;
在宣告之後,定義之前,只知道Screen是一個類名,但不知道包含哪些成員。只能以有限方式使用它,不能定義該型別的物件,只能用於定義指向該型別的指標或引用,宣告(不是定義)使用該型別作為形參型別或返回型別的函式。
void Test1(Screen& a){};
void Test1(Screen* a){};
4.2 類定義(define)
在建立類的物件之前,必須完整的定義該類,而不只是宣告類。所以,類不能具有自身型別的資料成員,但可以包含指向本類的指標或引用。
class LinkScreen
{
public:
Screen window;
LinkScreen* next;
LinkScreen* prev;
}; //注意,分號不能丟
因為在類定義之後可以接一個物件定義列表,可類比內建型別,定義必須以分號結束:
class LinkScreen{ /* ... */ };
class LinkScreen{ /* ... */ } scr1,scr2;
5 類物件
定義類物件時,將為其分配儲存空間。
Sales_item item; //編譯器分配了足以容納一個 Sales_item 物件的儲存空間。item 指的就是那個儲存空間。
6 隱含的 this 指標
成員函式具有一個附加的隱含形參,即 this指標,它由編譯器隱含地定義。成員函式的函式體可以顯式使用 this 指標。
6.1 何時使用 this 指標
當我們需要將一個物件作為整體引用而不是引用物件的一個成員時。最常見的情況是在這樣的函式中使用 this:該函式返回對呼叫該函式的物件的引用。
class Screen
{
...
public:
Screen& set(char);
};
Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
7 類作用域
每個類都定義了自己的作用域和唯一的型別。
類的作用域包括:類的內部(花括號之內), 定義在類外部的成員函式的引數表(小括號之內)和函式體(花括號之內)。
class Screen
{
//類的內部
...
};
//類的外部
char Screen::get(index r, index c) const
{
index row = r * width; // compute the row location
return contents[row + c]; // offset by c to fetch specified character
}
注意:成員函式的返回型別不一定在類作用域中。可通過 類名::來判斷是否是類的作用域,::之前不屬於類的作用域,::之後屬於類的作用域。例如
Screen:: 之前的返回型別就不在類的作用域,Screen:: 之後的函式名開始到函式體都是類的作用域。
class Screen
{
public:
typedef std::string::size_type index;
index get_cursor() const;
};
Screen::index Screen::get_cursor() const //注意:index前面的Screen不能少
{
return cursor;
}
該函式的返回型別是 index,這是在 Screen 類內部定義的一個型別名。在類作用域之外使用,必須用完全限定的型別名 Screen::index 來指定所需要的 index 是在類 Screen 中定義的名字。
二 建構函式
建構函式是特殊的成員函式,用來保證每個物件的資料成員具有合適的初始值。
建構函式名字與類名相同,不能指定返回型別(也不能定義返回型別為void),可以有0-n個形參。
在建立類的物件時,編譯器就執行一個建構函式。
1 建構函式可以過載
可以為一個類宣告的建構函式的數量沒有限制,只要每個建構函式的形參表是唯一的。
class Sales_item;
{
public:
Sales_item(const std::string&);
Sales_item(std::istream&);
Sales_item(); //預設建構函式
};
2 建構函式自動執行
只要建立該型別的一個物件,編譯器就執行一個建構函式:
Sales_item item1("0-201-54848-8");
Sales_item *p = new Sales_item();
第一種情況下,執行接受一個 string 實參的建構函式,來初始化變數item1。
第二種情況下,動態分配一個新的 Sales_item 物件,通過執行預設建構函式初始化該物件。
3 建構函式初始化式
與其他函式一樣,建構函式具有名字、形參表和函式體。
與其他函式不同的是,建構函式可以包含一個建構函式初始化列表:
Sales_item::Sales_item(const string &book): isbn(book), units_sold(0), revenue(0.0)
{ }
建構函式初始化列表以一個冒號開始,接著是一個以逗號分隔的資料成員列表,每個資料成員後面跟一個放在圓括號中的初始化式。
建構函式可以定義在類的內部或外部。建構函式初始化只在建構函式的定義中指定。
建構函式分兩個階段執行:(1)初始化階段;(2)普通的計算階段。初始化列表屬於初始化階段(1),建構函式函式體中的所有語句屬於計算階段(2)。
初始化列表比建構函式體先執行。不管成員是否在建構函式初始化列表中顯式初始化,類型別的資料成員總是在初始化階段初始化。
3.1 哪種類需要初始化式
const 物件或引用型別的物件,可以初始化,但不能對它們賦值,而且在開始執行建構函式的函式體之前要完成初始化。
初始化 const 或引用型別資料成員的唯一機會是建構函式初始化列表中,在建構函式函式體中對它們賦值不起作用。
沒有預設建構函式的類型別的成員,以及 const 或引用型別的成員,必須在初始化列表中完成初始化。
class ConstRef
{
public:
ConstRef(int ii);
private:
int i;
const int ci;
int &ri;
};
ConstRef::ConstRef(int ii)
{
i = ii; // ok
ci = ii; // error
ri = i; //
}
應該這麼初始化:
ConstRef::ConstRef(int ii): i(ii), ci(i), ri(ii) { }
3.2 成員初始化的次序
每個成員在建構函式初始化列表中只能指定一次。重複初始化,編譯器一般會有提示。
成員被初始化的次序就是定義成員的次序,跟初始化列表中的順序無關。
3.3 初始化式表示式
初始化式可以是任意表達式
Sales_item(const std::string &book, int cnt, double price): isbn(book), units_sold(cnt), revenue(cnt * price) { }
3.4 類型別的資料成員的初始化式
初始化類型別的成員時,要指定實參並傳遞給成員型別的一個建構函式,可以使用該型別的任意建構函式。
Sales_item(): isbn(10, '9'), units_sold(0), revenue(0.0) {}
3.5 類物件的資料成員的初始化
在類A的建構函式初始化列表中沒有顯式提及的每個成員,使用與初始化變數相同的規則來進行初始化。
類型別的資料成員,執行該型別的預設建構函式來初始化。
內建或複合型別的成員的初始值依賴於該類物件的作用域:在區域性作用域中不被初始化,在全域性作用域中被初始化為0。假設有一個類A,
class A
{
public:
int ia;
B b;
};
A類物件A a;不管a在區域性作用域還是全域性作用域,b使用B類的預設建構函式來初始化,ia的初始化取決於a的作用域,a在區域性作用域,ia不被初始化,a在全域性作用域,ia初始化0。
4 預設建構函式
不含形參的建構函式就是預設建構函式。
只要定義一個物件時沒有提供初始化式,就使用預設建構函式。如: A a;
為所有形參提供預設實參的建構函式也定義了預設建構函式。例如:
class A
{
public:
A(int a=1,char c =''){}
private:
int ia;
char c1;
};
4.1 合成的預設建構函式
只有當一個類沒有定義建構函式時,編譯器才會自動生成一個預設建構函式。
一個類只要定義了一個建構函式,編譯器也不會再生成預設建構函式。
建議:
如果定義了其他建構函式,也提供一個預設建構函式。
如果類包含內建或複合型別(如 int& 或 string*)的成員,它應該定義自己的建構函式來初始化這些成員。每個建構函式應該為每個內建或複合型別的成員提供初始化。
5 隱式類型別轉換
5.1 只含單個形參的建構函式能夠實現從形參型別到該類型別的一個隱式轉換
class A
{
public:
A(int a)
{
ia =a;
}
bool EqualTo(const A& a)
{
return ia == a.ia;
}
private:
int ia;
};
A a(1);
bool bEq = false;
bEq = a.EqualTo(1);//引數為1,實現從int型到A的隱式轉換
5.2抑制由建構函式定義的隱式轉換
通過將建構函式宣告為 explicit,來防止在需要隱式轉換的上下文中使用建構函式:
class A
{
public:
explicit A(int a )
{
ia =a;
}
bool EqualTo(const A& a)
{
return ia == a.ia;
}
private:
int ia;
};
通常,除非有明顯的理由想要定義隱式轉換,否則,單形參建構函式應該為 explicit。將建構函式設定為 explicit 可以避免錯誤。
三 複製控制
1 複製建構函式
1.1 幾個要點
(1) 複製建構函式
複製建構函式是一種特殊建構函式,只有1個形參,該形參(常用 const &修飾)是對該類型別的引用。
class Peopel
{
public:
Peopel();//預設建構函式
Peopel(const Peopel&);//複製建構函式
~Peopel();//解構函式
};
當定義一個新物件並用一個同類型的物件對它進行初始化時,將顯式使用複製建構函式。
Peopel a1; Peopel a2 = a1;
當將該型別的物件傳遞給函式或函式返回該型別的物件時,將隱式使用複製建構函式。
Peopel Func(Peopel b){...}
(2)解構函式
解構函式是建構函式的互補:當物件超出作用域或動態分配的物件被刪除時,將自動應用解構函式。
解構函式可用於釋放構造物件時或在物件的生命期中所獲取的資源。
不管類是否定義了自己的解構函式,編譯器都自動執行類中非 static 資料成員的解構函式。
(3) 複製控制
複製建構函式、賦值操作符和解構函式總稱為複製控制。編譯器自動實現這些操作,但類也可以定義自己的版本。
(4) 兩種初始化形式
C++ 支援兩種初始化形式:直接初始化和複製初始化。直接初始化將初始化式放在圓括號中,複製初始化使用 = 符號。
對於內建型別,例如int, double等,直接初始化和複製初始化沒有區別。
對於類型別:直接初始化直接呼叫與實參匹配的建構函式;複製初始化先使用指定建構函式建立一個臨時物件,然後用複製建構函式將那個臨時物件複製到正在建立的物件。直接初始化比複製初始化更快。
(5)形參和返回值
當形參或返回值為類型別時,由該類的複製建構函式進行復制。
(6)初始化容器元素
複製建構函式可用於初始化順序容器中的元素。例如:
vector<string> svec(5);
編譯器首先使用 string 預設建構函式建立一個臨時值,然後使用複製建構函式將臨時值複製到 svec 的每個元素。
(7)建構函式與陣列元素
如果沒有為類型別陣列提供元素初始化式,則將用預設建構函式初始化每個元素。
如果使用常規的花括號括住的陣列初始化列表來提供顯式元素初始化式,則使用複製初始化來初始化每個元素。根據指定值建立適當型別的元素,然後用複製建構函式將該值複製到相應元素:
Sales_item primer_eds[] = { string("0-201-16487-6"),
string("0-201-54848-8"),
string("0-201-82470-1"),
Sales_item()
};
1.2 合成的複製建構函式
(1)合成的複製建構函式
如果沒有定義複製建構函式,編譯器就會為我們合成一個。
合成複製建構函式的行為是,執行逐個成員初始化,將新物件初始化為原物件的副本。
逐個成員初始化:合成複製建構函式直接複製內建型別成員的值,類型別成員使用該類的複製建構函式進行復制。
例外:如果一個類具有陣列成員,則合成複製建構函式將複製陣列。複製陣列時合成複製建構函式將複製陣列的每一個元素。
1.3 定義自己的複製建構函式
(1) 只包含類型別成員或內建型別(但不是指標型別)成員的類,無須顯式地定義複製建構函式,也可以複製。
class Peopel
{
public:
std::string name;
unsigned int id;
unsigned int age;
std::string address;
};
(2) 有些類必須對複製物件時發生的事情加以控制。
例如,類有一個數據成員是指標,或者有成員表示在建構函式中分配的其他資源。而另一些類在建立新物件時必須做一些特定工作。這兩種情況下,都必須定義自己的複製建構函式。
最好顯式或隱式定義預設建構函式和複製建構函式。如果定義了複製建構函式,必須定義預設建構函式。
1.4 禁止複製
有些類需要完全禁止複製。例如,iostream 類就不允許複製。延伸:容器內元素不能為iostream
為了防止複製,類必須顯式宣告其複製建構函式為 private。
2 賦值操作符
與複製建構函式一樣,如果類沒有定義自己的賦值操作符,則編譯器會合成一個。
(1)過載賦值操作符
Sales_item& operator=(const Sales_item &);
(2)合成賦值操作符
合成賦值操作符會逐個成員賦值:右運算元物件的每個成員賦值給左運算元物件的對應成員。除陣列之外,每個成員用所屬型別的常規方式進行賦值。對於陣列,給每個陣列元素賦值。
(3)複製和賦值常一起使用
一般而言,如果類需要複製建構函式,它也會需要賦值操作符。
3 解構函式
建構函式的用途之一是自動獲取資源;與之相對的是,解構函式的用途之一是回收資源。除此之外,解構函式可以執行任意類設計者希望在該類物件的使用完畢之後執行的操作。
(1) 何時呼叫解構函式
撤銷(銷燬)類物件時會自動呼叫解構函式。
變數(類物件)在超出作用域時應該自動撤銷(銷燬)。
動態分配的物件(new A)只有在指向該物件的指標被刪除時才撤銷(銷燬)。
撤銷(銷燬)一個容器(不管是標準庫容器還是內建陣列)時,也會執行容器中的類型別元素的解構函式(容器中的元素總是從後往前撤銷)。
(2)何時編寫顯式解構函式
如果類需要定義解構函式,則它也需要定義賦值操作符和複製建構函式,這個規則常稱為三法則:如果類需要解構函式,則需要所有這三個複製控制成員。
(3)合成解構函式
合成解構函式按物件建立時的逆序撤銷每個非 static 成員,因此,它按成員在類中宣告次序的逆序撤銷成員。
對於每個類型別的成員,合成解構函式呼叫該成員的解構函式來撤銷物件。
合成解構函式並不刪除指標成員所指向的物件。 所以,如果有指標成員,一定要定義自己的解構函式來刪除指標。
解構函式與複製建構函式或賦值操作符之間的一個重要區別:即使我們編寫了自己的解構函式,合成解構函式仍然執行。
四 友元
友元機制允許一個類將對其非公有成員的訪問權授予指定的函式或類。
友元可以出現在類定義的內部的任何地方。
友元不是授予友元關係的那個類的成員,所以它們不受宣告出現部分的訪問控制影響。
建議:將友元宣告成組地放在類定義的開始或結尾。
1 友元類
class Husband
{
public:
friend class Wife;
private:
double money;//錢是老公私有的,別人不能動,但老婆除外
};
class Wife
{
public:
void Consume(Husband& h)
{
h.money -= 10000;//老婆可以花老公的錢
}
};
Husband h;
Wife w;
w.Consume(h);
2 使其他類的成員函式成為友元
class Husband; //1.宣告Husband
class Wife //2.定義Wife類
{
public:
void Consume(Husband& h);
};
class Husband //3.定義Husband類
{
public:
friend void Wife::Consume(Husband& h);//宣告Consume函式。
private:
double money;//錢是老公私有的,別人不能動,但老婆除外
};
void Wife::Consume(Husband& h) //4.定義Consume函式。
{
h.money -= 10000;//老婆可以花老公的錢
}
注意類和函式的宣告和定義的順序:
(1)宣告類Husband
(2)定義類Wife,宣告Consume函式
(3)定義類Husband
(4)定義Consume函式。
五 static 類成員
static 成員,有全域性物件的作用,但又不破壞封裝。
1 static 成員變數
static 資料成員是與類關聯的物件,並不與該類的物件相關聯。
static 成員遵循正常的公有/私有訪問規則。
2 使用 static 成員而不是全域性物件有三個優點。
(1) static 成員的名字是在類的作用域中,因此可以避免與其他類的成員或全域性物件名字衝突。
(2) 可以實施封裝。static 成員可以是私有成員,而全域性物件不可以。
(3) 通過閱讀程式容易看出 static 成員是與特定類關聯的,這種可見性可清晰地顯示程式設計師的意圖。
3 static 成員函式
在類的內部宣告函式時需要新增static關鍵字,但是在類外部定義函式時就不需要了。
因為static 成員是類的組成部分但不是任何物件的組成部分,所以有以下幾個特點:
1) static 函式沒有 this 指標
2) static 成員函式不能被宣告為 const (將成員函式宣告為 const 就是承諾不會修改該函式所屬的物件)
3) static 成員函式也不能被宣告為虛擬函式
4 static 資料成員
static 資料成員可以宣告為任意型別,可以是常量、引用、陣列、類型別,等等。
static 資料成員必須在類定義體的外部定義(正好一次),並且應該在定義時進行初始化。
建議:定義在類的原始檔中名,即與類的非行內函數的定義同一個檔案中。注意,定義時也要帶上類型別+"::"
double Account::interestRate = 0.035;
5 特殊的靜態常量整型成員
靜態常量整型資料成員可以直接在類的定義體中進行初始化,例如:
static const int period = 30;
當然char 可以轉換成整形,也是可以的, static const char bkground = '#';
6 其他
(1)static 資料成員的型別可以是該成員所屬的類型別。非 static 成員只能是自身類物件的指標或引用
class Screen
{
public:
// ...
private:
static Screen src1; // ok
Screen *src2; // ok
Screen src3; // error
};
(2)非 static 資料成員不能用作預設實參,static 資料成員可用作預設實參
class Screen
{
public:
Screen& clear(char = bkground);
private:
static const char bkground = '#';//static const整形變數可以在類內部初始化。
};
========
C++建立物件的三種方式
http://blog.csdn.net/azhexg/article/details/14225545C++中有三種建立物件的方法
#include <iostream>
using namespace std;
class A
{
private:
int n;
public:
A(int m):n(m)
{ }
~A(){}
};
int main()
{
A a(1); //棧中分配
A b = A(1); //棧中分配
A* c = new A(1); //堆中分配
delete c;
return 0;
}
第一種和第二種沒什麼區別,一個隱式呼叫,一個顯式呼叫,兩者都是在程序虛擬地址空間中的棧中分配記憶體,而第三種使用了new,在堆中分配了記憶體,而棧中記憶體的分配和釋放是由系統管理,而堆中記憶體的分配和釋放必須由程式設計師手動釋放。採用第三種方式時,必須注意一下幾點問題:
new建立類物件需要指標接收,一處初始化,多處使用
new建立類物件使用完需delete銷燬
new建立物件直接使用堆空間,而區域性不用new定義類物件則使用棧空間
new物件指標用途廣泛,比如作為函式返回值、函式引數等
頻繁呼叫場合並不適合new,就像new申請和釋放記憶體一樣
棧的大小遠小於堆的大
棧是機器系統提供的資料結構,計算機會在底層對棧提供支援:分配專門的暫存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率 比較高。堆則是C/C++函式庫提供的,它的機制是很複雜的,例如為了分配一塊記憶體,庫函式會按照一定的演算法(具體的演算法可以參考資料結構/作業系統)在 堆記憶體中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由於記憶體碎片太多),就有可能呼叫系統功能去增加程式資料段的記憶體空間,這樣就有機會 分 到足夠大小的記憶體,然後進行返回。顯然,堆的效率比棧要低得多。
========
C++用new來建立物件和非new來建立物件的區別
http://www.cnblogs.com/GODYCA/archive/2013/01/10/2854777.html我們都知道C++中有三種建立物件的方法,如下:
#include <iostream>
using namespace std;
class A
{
private:
int n;
public:
A(int m):n(m)
{
}
~A(){}
};
int main()
{
A a(1); //棧中分配
A b = A(1); //棧中分配
A* c = new A(1); //堆中分配
delete c;
return 0;
}
第一種和第二種沒什麼區別,一個隱式呼叫,一個顯式呼叫,兩者都是在程序虛擬地址空間中的棧中分配記憶體,而第三種使用了new,在堆中分配了記憶體,而棧中記憶體的分配和釋放是由系統管理,而堆中記憶體的分配和釋放必須由程式設計師手動釋放,所以這就產生一個問題是把物件放在棧中還是放在堆中的問題,這個問題又和堆和棧本身的區別有關:
這裡面有幾個問題:
1.堆和棧最大可分配的記憶體的大小
2.堆和棧的記憶體管理方式
3.堆和棧的分配效率
首先針對第一個問題,一般來說對於一個程序棧的大小遠遠小於堆的大小,在linux中,你可以使用ulimit -s (單位kb)來檢視一個程序棧的最大可分配大小,一般來說不超過8M,有的甚至不超過2M,不過這個可以設定,而對於堆你會發現,針對一個程序堆的最大可分配的大小在G的數量級上,不同系統可能不一樣,比如32位系統最大不超過2G,而64為系統最大不超過4G,所以當你需要一個分配的大小的記憶體時,請用new,即用堆。
其次針對第二個問題,棧是系統資料結構,對於程序/執行緒是唯一的,它的分配與釋放由作業系統來維護,不需要開發者來管理。在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時,這些儲存單元會被自動釋放。棧記憶體分配運算內置於處理器的指令集中,效率很高,不同的作業系統對棧都有一定的限制。 堆上的記憶體分配,亦稱動態記憶體分配。程式在執行的期間用malloc申請的記憶體,這部分記憶體由程式設計師自己負責管理,其生存期由開發者決定:在何時分配,分配多少,並在何時用free來釋放該記憶體。這是唯一可以由開發者參與管理的記憶體。使用的好壞直接決定系統的效能和穩定。
由上可知,但我們需要的記憶體很少,你又能確定你到底需要多少記憶體時,請用棧。而當你需要在執行時才知道你到底需要多少記憶體時,請用堆。
最後針對第三個問題,棧是機器系統提供的資料結構,計算機會在底層對棧提供支援:分配專門的暫存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率 比較高。堆則是C/C++函式庫提供的,它的機制是很複雜的,例如為了分配一塊記憶體,庫函式會按照一定的演算法(具體的演算法可以參考資料結構/作業系統)在 堆記憶體中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由於記憶體碎片太多),就有可能呼叫系統功能去增加程式資料段的記憶體空間,這樣就有機會 分 到足夠大小的記憶體,然後進行返回。顯然,堆的效率比棧要低得多。
由上可知,能用棧則用棧。
複製程式碼
#include <stdio.h>
#include <stdlib.h>
void main()
{
int n,*p,i,j,m;
printf("本程式可對任意個整數排序;\n");
printf("請輸入整數的總個數: ");
scanf("%d",&n);
p=(int *)calloc(n,sizeof(int)); //執行時決定記憶體分配大小
if(p==0) {
printf("分配失敗!\n");
exit(1);
}
========
相關推薦
C++類和物件學習總結
C++中的類與物件 http://blog.csdn.net/qq_32583189/article/details/52412369 C++中的類與物件 C中的類與物件 定義類 建立標頭檔案 類和結構體 訪問控制 作用域解析運算子 實現類成員 預設的內聯方法 建立物件 開
[C++]類和物件學習總結
本文章分為知識點、例子和心得基礎知識:必須在定義了類之後,才可以定義類的物件,物件是類的例項或實體。物件成員的訪問:1、圓點訪問形式:物件名.公有成員(例:) 2、指標訪問形式:物件指標變數名->公有成員(例
C++類和物件學習筆記(3)
1.初始化列表 a.建構函式體賦值 在建立物件時,編譯器可以通過呼叫建構函式,給物件中各個成員變數一個合適的初始值。但是雖然建構函式呼叫之後,物件中已經有了一個初始值,但是不能將其稱作為類物件成員的
c++中類和物件的總結
類和物件的區別 類是抽象的,物件是具體的,所以,類不佔用記憶體,而物件佔用記憶體。總之一句話,類是物件的抽象,物件是類的具體事例。 例如:類是水果的話,那麼物件就是蘋果…… 類中的函式 1、類的成員函式:是指把函式的原型和定義寫在類的內部的函式。是類
C++類和物件總結
類和物件的區別: 類是抽象的,物件是具體的,所以類不佔用記憶體,而物件佔用記憶體; 總之類是物件的抽象,而物件是類的具體事例 假如:類是水果,那麼物件就是香蕉… 面向物件的三大特點: 封裝、繼承、多型 類的三種訪問限定符: 1. public(公
C++類和物件(下篇)
1. 再談建構函式 2. static成員 3.C++11的成員初始化新玩法 4. 友元 5.內部類 6. 再次理解封裝 1. 再談建構函式 1.1建構函式體賦值 在建立物件時,編譯器通過呼叫建構函式,給物件中各個成員變數一個合適的初始值
C++類和物件.四個預設成員函式(賦值運算子過載)
1.(1)類的定義 類就是具有相同資料和相同操作的一組物件的集合。 學生類定義: class student {//成員變數char* name;int age;int sex;//成員函式void speak(){cout<<name<<"年
C++類和物件(一)&&實現OFFSETOF巨集&&THIS指標
一.目錄 1.物件的相關知識 2.類的定義 3.類的例項化 4.類物件模型 5.模擬實現offsetof巨集 6.this指標 二.正文 1.物件的相關知識 C語言是面向過程的,關注的是過程,分析求解問題的步驟,通過函式呼叫逐步解決問題
C++類和物件知識點整理1
第1題:請寫出下面這個類的方法程式碼 #include<iostream> #include<string> using namespace std; #pragma warning (disable:4996) //第1題: class String { public:
c++類和物件(2)
1:設計類就是設計型別 1)我們給這個賦予什麼合法值。2):類中包含什麼樣的函式和操作符。 3:新型別的物件該如何被建立和銷燬。4:物件的初始化和賦值。 5:物件作為函式的引數如何以值傳遞。6:誰使用此型別的物件成員。 類的成員public:公有型別成員 在關鍵詞public後面宣告:它
【C++】C++類和物件的概念
C++中的類也是一種構造型別,但是進行了一些擴充套件,類的成員不但可以是變數,還可以是函式;通過類定義出來的變數也有特定的稱呼,叫做“物件”。 通過結構體定義出來的變數還是叫變數,而通過類定義出來的變數有了新的名稱,叫做物件(Object)。 有些資料也將類的成員
C++類和物件(上篇)
類與物件(上) 1. 類與物件的初步認知 2. 類的引入 3. 類的定義 4. 類的作用域 5. 類的例項化 6. 類的訪問限定符及封裝 7. 類的物件大小的計算 8. 類成員函式的this指標 1. 類與物件的初步認知 C語言是面向過程的,關注的是過
對C++類和物件的簡單認識
C++類的定義: 類是定義同一類所有物件的變數和方法的藍圖或原型 類也可以定義類變數和類方法。可以從類的例項中或者直接從類中訪問類變數和方法。類方法只能操作類變數,不必訪問例項變數或例項方法。系統在第一次在程式中遇到一個類時為這個類建立它的所有類變數的拷貝,這個類的所有例項共
c++| |類和物件(上篇)
類和物件(上篇) 1.類和物件的初步認知 c語言是面向過程的,關注的是過程,分析出求解問題的步驟,通過函式呼叫逐步解決問題 c++是基於面向物件的,關注的是物件,將一件事情拆分成不同的物件,考物件之間的互動完成 2.類的引入 c語言中,結構
C++類和物件(一)
1.物件的相關知識 C語言是面向過程的,關注的是過程,分析求解問題的步驟,通過函式呼叫逐步解決問題。 C++是面向物件的,關注的是物件,將一件事拆分成不同的物件,靠物件之間的互動完成。 物件:任何一個物件都應該具有兩個要素,即屬性和行為,物件是由一組屬性和行為構成的。如現實生活中的手機就是一個物
C++類和物件(一)&&實現OFFSETOF巨集&&THIS指標
一.目錄 1.物件的相關知識 2.類的定義 3.類的例項化 4.類物件模型 5.模擬實現offsetof巨集 6.this指標 二.正文 1.物件的相關知識 C語言是面向過程的,關注的是過程,分析求解問題的步驟,通過函式呼叫逐步解
C++ 類和物件(上)
目錄 類 類的引入 類的定義 訪問限定符 封裝 類的作用域 類的例項化 引出 C語言是面向過程的,關注的是過程,分析出求解問題的步驟,通過函式呼叫逐步解決問題。C++是基於面向物件的,關注的是物件,將一件事情拆分成不同的物件,靠物
C++類和物件詳解(new與不new的區別)
一、"類" 的介紹 在C++中, 用 "類" 來描述"物件", 所謂的"物件"是指現實世界中的一切事物。那麼類就可以看做是對相似事物的抽象, 找到這些不同事物間的共同點, 如自行車和摩托車, 首先他們都屬於"物件", 並且具有一定得相同點, 和一些不同點, 相同點如
C++類和物件例項解析
C++既是面向物件也是面向過程的語言,在這裡就有一個重要的概念——類。 何謂類?類是對物件的一種抽象,舉例來講:每一個實實在在存在的人就是一個物件,人有很多共同的特徵(一個頭,兩條腿,能走,能跑),這具有共同特徵的人就成為一個類。類是一個抽象的名詞,每一個人(即物件)是這個類的例項。
C++類和物件概念
C++是一門面向物件的程式語言,理解C++,首先要理解類與物件這兩個概念。 C++中的類可以看做C語言中結構體(Struct)的升級版。結構體是一種構造資料型別,可以包含若干成員(變數),每個成員的資料型別可以不一樣;可以通過結構體來定義結構體變數,每個變數擁有相同的性質。