【c++】類與對象詳解

目錄

  • 面向過程思想和面向對象思想
  • 類的定義
    • 引入類的關鍵字
    • 類定義的兩種方式
    • 類的訪問限定符
    • 類的作用域
    • 類大小的計算
    • 封裝
  • this指針
  • 類的6個默認成員函數
    • 構造函數
      • 初步理解構造函數
      • 深入理解構造函數
        • 初始化列表
        • 單參數構造函數引發的隱式類型轉換
    • 析構函數
    • 拷貝構造函數
    • 賦值運算符重載
      • 運算符重載
      • 賦值運算符重載
    • 取地址及const取地址操作符重載
      • const成員函數
      • 取地址及const取地址操作符重載
  • static成員
  • 友元
    • 友元函數
    • 友元類
  • 內部類
  • 什么是類與對象

面向過程思想和面向對象思想

c++作為一門高級語言,相較于c語言其引入了類和對象的概念,是一門面向對象的語言,而c語言是一門面向過程的語言。c語言作為一門面向過程的語言,面對問題更關注解決問題的過程,將問題拆解為一個個小步驟,逐個解決。而c++作為一門面向對象的語言,面對問題時更注重問題的對象,靠對象之間的交互解決問題。

類的定義

class _jiunian
{
public://……
protected://……
private://……
};

class是定義類的關鍵字,_jiunian是類的名字(憑喜好自己取),大括號中可以定義函數,變量,自定義類型。可以通過類訪問限定符限制成員的訪問條件。注意末尾分號不能省!

引入類的關鍵字

引入類最為常見的關鍵字是class和struct,兩者的區別是struct定義的類默認情況下(不使用訪問限定符)成員是公有的(public),即類的內部和類的外部都可以訪問;而class定義的類默認情況下成員是私有的(private)(兼容c語言),即只有類的內部才可以訪問。

類定義的兩種方式

常見的類的定義方式有兩種,一種是所有的成員全部完整定義在類的內部,一種是對于成員函數這種代碼量較大的部分,采用聲明定義分離,只將聲明留在類中,再將類定義在頭文件中。對于函數的定義,我們將其定義在源文件中,引入頭文件到源文件中就行。這樣做的好處就是類中的函數一行一個,找接口方便,而不是一個函數一大行,找個函數接口鼠標滾邊天找不到。所以成員函數聲明定義分離的定義方式是比較推薦的。

類的訪問限定符

訪問限定符可以限制成員的訪問條件。訪問限定符的效果持續到下一個訪問限定符出現位置為止,如果之后沒有訪問限定符出現,就持續到結束位置。訪問限定符有三種,分別是public、protected、private。public的效果是使成員變為公有,即類內類外都可以訪問。protected和private則可以使成員變為私有,即只有類內可以訪問,類外不可以訪問。而其兩者之間也有區別,當存在繼承關系時,子類可以訪問父類中的protected標識的成員,而不能訪問父類中的private表示的成員。

類的作用域

c++中引入了作用域的概念,所有類的成員都得在類的作用域中。如果出現之前所說的函數的聲明和定義分離的情況,那么在類外定義函數時有加上作用域解析符表明是哪個類域的。

類大小的計算

類的成員有很多種,但其實實際存儲的只有類的成員變量。成員變量又稱為類的屬性,成員函數又稱為類的方法。由這樣的名字我們就能明白,因為對于同類型的類來說,它們之間的不同只有類的屬性也就是成員變量。類的方法也就是成員函數都是相同的,相同的東西反復存儲未免浪費空間,所以這些成員函數實際存在公共的代碼段,即使定義多個相同類型的類,成員函數也就只有一組供這些類共同使用。刨去成員函數,對于這些類所占用的空間的計算方法與c語言中的結構體一樣,這里不做過多贅述。需要注意的是,類的定義不會占用空間,只用真正定義了類類型的變量類才能真正實體化。即使是空類也是會占用一個字節的空間來占位的。

封裝

類實際上就是對對象封裝的結果,通過封裝,隱藏對象的屬性以及細節,控制對外開放的接口數量與對象進行交互。

this指針

class _jiunian
{
public:_jiunian(int a, int b, int c){_a = a;_b = b;_c = c;}void reset(int a, int b, int c){_a = a;_b = b;_c = c;}
private:int _a;int _b;int _c;
};int main()
{_jiunian x(1, 1, 1), y(2, 2, 2);x.reset(1, 2, 3);y.reset(1, 2, 3);return 0;
}

正如筆者之前所說的一樣,類的成員函數只保存一份且存在公共代碼段,所以無論是x.reset還是y.reset調用的都是同一個函數,那對于只傳了數值而沒有傳遞地址的函數是怎么識別要操作的類是哪一個的呢?實際上,c++編譯器給每一個非靜態成員函數隱式傳遞了一個this指針,這個指針是 類類型*const 類型,即指向類類型變量,其本身不能改變的指針變量。需要注意的是:this指針不能被賦值,因為被const修飾了;this指針只能再成員函數內部使用,寫在除此之外的任何地方都會報錯,this本身也是c++中的一個關鍵字,不能將變量起這個名字;this指針不會存在對象中,this指針在類的成員函數被調用時由編譯器自動識別傳參;this指針在現在的編譯器優化下為達到最佳速度,一般是存在寄存器中,因為要反復調用;使用空指針調用類的成員函數時this指針也可以為空,不過這時函數中倘若出現對this指針解引用的操作就會報錯;在成員函數中對this指針解引用的操作可以將this省略,直接寫成員的名字。

類的6個默認成員函數

在類中有6個如果我們沒有顯式定義,編譯器也會自動生成的函數。這些成員函數稱為默認函數。

構造函數

class jiunian
{
public:jiunian(){_a = 1;_b = 1;_c = 1;}jiunian(int a, int b, int c){_a = a;_b = b;_c = c;}
private:int _a;int _b;int _c;
};int main()
{jiunian x;//不可以寫成jiunian x(),這樣會被當成函數的聲明!jiunian y(1, 2, 3);return 0;
}

初步理解構造函數

構造函數是一個特殊的成員函數,他直接由類名來命名,且沒有返回值,它在創建類類型對象時被編譯器自動調用,保證每個成員變量都有一個合適的初始值,構造函數本身并不會起到構造對象的作用,構造對象是由系統開辟空間構造的,構造函數本身只起到對類類型對象的成員變量進行初始化的作用。構造函數本身也支持函數重載。較為常見的是重載兩個函數,一個無參,一個有參,這樣可以在類類型對象創建時選擇自定義對象的成員變量初始值,也可以直接創建對象,這時對象的成員變量就是設置好的默認值,當然我們也可以直接通過全缺省函數完成這一操作。當我們沒有顯式創建構造函數時,編譯器會默認自己生成一個默認構造函數以供創建類類型對象時使用,編譯器生成的構造函數是默認構造函數,默認構造函數是指在沒有任何參數時會調用的函數,無參構造函數或者全缺省構造函數都是默認構造函數,并不是只有編譯器自己生成的才叫默認構造函數(默認構造函數只能有一個)。那么,說到這里,既然編譯器自己會生成默認構造構造函數,我們是不是就不用自己顯式定義了呢?其實不是,雖然編譯器會自己生成,但它也不會智慧到會猜得到我們想要將成員變量設置成什么數,所以我們還是有需要手動設置的情況。具體來說,編譯器自動生成的默認構造函數不會對內置類型(c++自帶的類型,像int、double、short這種)進行初始化(不初始化就是隨機值),因為編譯器自己也不會知道究竟要初始化成什么才好,這只能由我們自己來設置。但倘若是自定義類型,編譯器生成的默認構造函數會自動調用自定義類型自己的構造函數來進行初始化,畢竟只能靠這個來初始化,編譯器不會有任何質疑。當然,內置類型不會默認初始化,我們又懶得顯式定義時,我們可以在聲明變量時加上缺省值,這樣不用顯式定義也能完成對內置類型的初始化。總的來說,如果一個類中的成員變量都是自定義類,我們就不用自己寫構造函數,編譯器自己生成的就能解決;若一個類中的成員變量中有內置類型,就需要我們自己寫構造函數或者給出缺省值來解決。

深入理解構造函數

通過初步理解構造函數,其用法我們已經了然于胸,但對于構造函數本質的理解還是不夠的。

初始化列表

我們在構造函數的函數名和參數的后面可以加上一個冒號,后面是以逗號分隔的成員變量列表,每個變量后面可以加上括號,括號中是想要初始化的值,通過初始化列表。我們也能完成對類的成員變量的初始化操作。

class jiunian
{
public:jiunian():_a(2),_b(2),_c(2)//初始化列表{_a = 1;_a = 2;_b = 1;_c = 1;}jiunian(int a, int b, int c){_a = a;_b = b;_c = c;}
private:int _a = 0;int _b = 0;int _c = 0;
};int main()
{jiunian x;return 0;
}

又是初始化列表又是構造函數又是缺省值的,只是初始化一下類卻有這么多種方法,倘若我既設置缺省值,又給出初始化列表,在構造函數中又給出賦值,最后函數會如何處理呢。跑一下代碼后我們會發現最后類中的成員變量的值是以類中的賦值為準。這是為什么呢?事實上,構造函數內的初始化,并不是真正意義上的初始化,它只是一種賦值,進一步說,是給成員變量賦初值,就像我上面的代碼中一樣,我可以對同一個變量多次賦初值,倘若是初始化,這種操作是不行的,因為變量只能初始化一次。而初始化列表才是真正的初始化,倘若我們在初始化列表對同一個變量多次初始化編譯器便會報錯。而缺省值所要真正傳遞給的對象其實是初始化列表,在我們沒有給出初始化列表的值會接受缺省值進行初始化。下面對類類型對象創建時構成進行詳細解釋。在類類型對象創建時,會優先使用構造函數的初始化列表對類類型對象進行初始化,若有成員變量沒有在初始化列表中出現,就會將改成員變量的缺省值傳給初始化列表進行初始化(如果有缺省值的情況下)。如果沒有在初始化列表中出現,也沒有缺省值,這時就會分為兩種情況進行處理:一種是該變量是內置類型,編譯器不會對內置類型進行初始化;一種是它是自定義類型,編譯器會自動調用它的構造函數進行初始化。這時我們就明白,對于內置類型和自定義類型的處理在初始化列表階段就已經完成。在初始化列表結束之后,才會進入構造函數內部對成員變量進行初賦值,所以成員變量的初始值最終是由構造函數的內部決定的,構造函數內部沒有初賦值就看初始化列表,初始化列表沒有就看缺省值,都沒有那就是隨機值了。
初始化列表的注意事項:
1.每個成員變量在初始化列表中只能出現一次(初始化只能初始化一次)
2. 類中包含以下成員,必須放在初始化列表位置進行初始化:
(1)引用成員變量
(2)const成員變量
(3)自定義類型成員(且該類沒有默認構造函數時)
我們可以發現,在初始化列表中,對于自定義成員來說,有默認構造函數不寫的話會自己調用,沒默認構造函數則必須要寫,總的來說,自定義成員必定會在初始化列表完成初始化。對于成員變量,能在初始化列表初始化就盡量在初始化列表初始化。因為對于內置類型來說,在構造函數內部初賦值,實際上是先初始化再賦值,因為即使編譯器在初始化列表對沒有顯式初始化的內置成員變量不會進行初始化,但它本質還是被創建了出來,只不過是隨機值。而如果在初始化列表初始化,就是直接初始化,效率更高。對于自定義類型就更是如此了,自定義類型必定在初始化列表初始化,這時再在構造函數內部賦值,還要調用賦值函數,占用不少資源,很虧。所以綜上盡量使用初始化列表,但這并不是意味著我們可以無腦使用初始化列表,有些情況初始化列表無法完成初始化,比如:

class jiunian
{
public:jiunian():_a(2),_b(2),_c(2){int* ptr = (int*)calloc(4, sizeof(int));if (ptr == nullptr){perror("jiunian:calloc");return;}_d = ptr;}
private:int _a;int _b;int _c;int* _d;
};int main()
{jiunian x;return 0;
}

這種要為指針動態開辟空間初始化的成員變量就不能直接用初始化列表初始化,當然這只是一種簡單場景,實際應用還會有個各種情況,我們應該靈活應對。最后還有一個初始化列表的小坑要說,就是初始化列表的初始化順序并不是按照初始化列表的順序來進行的,是成員變量在類中的定義順序來決定的。簡單來說就是,如果a變量在類中的定義位置在b變量之前,那在初始化列表中即使b的初始化寫在a之前,也是a先初始化,這點要注意,避免出現以下問題:

class shinku
{
public:shinku(){std::cout << "shinku" << std::endl;}
private:int _a;
};class jiunian
{
public:jiunian():_b(2),_a(_b),//_a先定義,_b此時是隨機值,_a被定義成隨機值_c(2){}
private:int _a;int _b;int _c;
};int main()
{jiunian x;return 0;
}
單參數構造函數引發的隱式類型轉換

在C語言中對內置類型之間的運算存在隱式類型轉換,在c++中對于類類型對象也有這種操作,比如以下的代碼是可以跑的起來的:

class jiunian
{
public:jiunian(int a){_a = a;}
private:int _a;
};int main()
{jiunian x = 1;return 0;
}

上面這組代碼能跑的起來就是因為1被隱式類型轉化成了類類型對象,再由轉換而來的臨時變量給x拷貝構造而來(但編譯器實際上并不是這么實現的,因為這個臨時變量由1隱式轉換而來,在拷貝構造給x后就被銷毀了,編譯器會覺得很浪費,直接優化成jiunian x(1),這樣就只有一次調用構造函數,節省資源,雖然最終不是這樣實現的,但這種優化還是因為有隱式類型轉換才得以存在)。在c++中的類只有當其構造函數可以只接受一個參數時才會引發隱式類型轉換(只有一個參數,或者除了第一個參數外都有缺省值,或者是全缺省),畢竟要是可以接收多個參數的話只用一個數怎么也引發不了。如果想要防止這樣的隱式類型轉換,可以在構造函數的前面加上explicit來限制這種隱式類型轉換。

析構函數

class jiunian
{
public:jiunian(int a, int b):_a(a),_b(b){int* ptr = (int*)calloc(4, sizeof(int));if (ptr == nullptr){perror("jiunian:calloc");}_c = ptr;}~jiunian(){_a = 0;_b = 0;free(_c);_c = nullptr;}
private:int _a;int _b;int* _c;
};int main()
{jiunian x(1, 1);return 0;
}

析構函數在對象銷毀時調用,析構函數沒有返回值,名字與類名一致但前面要加上~以和構造函數區分,析構函數沒有參數,畢竟銷毀類也不需要什么參數(this指針還是要的,但也不用自己寫出)。析構函數在類對象銷毀時自動調用,與構造函數一樣,雖然析構函數在函數銷毀時調用,但析構函數本身不會起到銷毀對象的作用,只會對類中的資源進行清理。析構函數在我們沒有顯式定義時編譯器會自動生成,自動生成的析構函數對于類中的內置類型對象不會進行處理,因為內置類型在程序結束時由系統回收處理就行,而對于自定義類型則會自動調用類的析構函數來進行資源清理。析構函數一般不用自己顯示定義,但當類的構造函數向系統申請了資源時,務必要顯示定義析構函數,系統自動生成的析構函數不會識別釋放你申請的資源,不寫會造成內存泄漏。

拷貝構造函數

class jiunian
{
public:jiunian(int a, int b):_a(a),_b(b){int* ptr = (int*)calloc(4, sizeof(int));if (ptr == nullptr){perror("jiunian_i_i:calloc");return;}_c = ptr;}jiunian(jiunian& x){std::cout << "jiunian" << std::endl;_a = x._a;_b = x._b;int* ptr = (int*)calloc(4, sizeof(int));if (ptr == nullptr){perror("jiunian_j:calloc");return;}_c = ptr;memcpy(_c, x._c, sizeof(_c));}~jiunian(){_a = 0;_b = 0;free(_c);_c = nullptr;}
private:int _a;int _b;int* _c;
};

拷貝構造函數,刨去拷貝,本質還是構造函數。拷貝構造函數是構造函數的一種重載形式。拷貝函數被用于用已有的類類型對象創建新的類類型對象時由編譯器自動調用使用。

jiunian x(1, 1);
jiunian y(x);
jiunian y = x;

使用這兩種方法都可以對y進行拷貝初始化,第一種是直接傳參調用,一種是用一個類類型對象初始化另一個。此外,我們還應注意的是,事實上在c++中,在函數傳參時,參數是會被拷貝一份傳進函數中的,這些局部變量會在函數結束時銷毀,所以類在函數傳參時也會調用拷貝構造函數,這也是為什么我們不能在拷貝函數傳參時直接傳值調用的原因,如果對拷貝構造函數傳值調用就會先去調用拷貝構造,然后因為要傳值所以又會調用,這會引發無窮遞歸,沒完沒了了,所以不行。拷貝函數如果我們不去顯式定義,編譯器會自動生成。編譯器自動生成的拷貝構造是淺拷貝,又稱值拷貝,會對拷貝的對象按存儲空間逐字節。這樣的拷貝方式對大部分情況都很合適,所以大部分情況拷貝函數可以不用寫,但是,老樣子,對于需要向系統申請空間的類,拷貝函數需要自己寫,不然會造成兩個同類型的類中的指針指向同一塊空間,程序結束類類型對象銷毀時重復釋放同一塊空間造成報錯。

賦值運算符重載

運算符重載

class jiunian
{
public:jiunian(int a, int b):_a(a),_b(b){}jiunian(jiunian& x){std::cout << "jiunian" << std::endl;_a = x._a;_b = x._b;}bool operator<(jiunian y){if (_a < y._a && _b < y._b){return true;}return false;}~jiunian(){_a = 0;_b = 0;}
private:int _a;int _b;
};int main()
{jiunian x(1, 1);jiunian y(2, 2);if (x < y)std::cout << "work" << std::endl;return 0;
}

c++為了增加類相關的代碼的可讀性,引入了運算符重載,運算符重載本質是一種特殊的函數,關鍵字是operator接要重載的運算符,注意中間沒有空格。函數定義的格式是:返回值類型 operator操作符(參數列表)。對于運算符重載函數,需要注意的是:
(1)不能通過連接其他符號來創建新的操作符:比如operator@
(2)重載操作符必須有一個類類型參數
(3)用于內置類型的運算符,其含義不能改變,例如:內置的整型+,不 能改變其含義
(4)作為類成員函數重載時,其形參看起來比操作數數目少1,因為成員函數的第一個參數為隱藏的this
(5).* :: sizeof ?: . 注意以上5個運算符不能重載。這個經常在筆試選擇題中出現。

對于運算符重載函數,遵循以上規定就能對于大部分雙目操作符進行定義,但對于單目操作符++(或–)這種前置后置有細微差別的操作符該怎么去定義呢?

	jiunian& operator++(){++_a;++_b;return *this;}jiunian& operator++(int){jiunian* const ret = this;++_a;++_b;return *ret;}

對于前置++和后置++,想要實現他們之間細微的不同毫無疑問要靠函數重載,但怎么重載成了問題,畢竟函數名一樣,函數參數也一樣,參數還只有一個this指針,根本沒法重載,這時c++特別規定在后置++函數的參數中加入int類型參數以跟前置++區分,這個int類型參數本身在函數中沒有任何作用,所以可以省略參數名。

賦值運算符重載

	jiunian& operator=(jiunian& y){if(this == &y){_a = y._a;_b = y._b;}return *this;}

與運算符重載函數不同的是,他是類的6個默認函數之一,即使不自己顯式定義,編譯器也會自己生成。因為是對已有的類類型對象進行操作,不必考慮局部變量銷毀產生野引用的問題,放心大膽的將參數和返回值都設置成引用,減少拷貝構造函數的調用,節省資源。if語句檢查是否是自己給自己賦值,進一步優化函數。因為內置類型的=運算符會返回賦值后的結果,這里的=重載也是同理。需要注意的是,由于賦值運算符重載是類的6個默認構造函數,所以如果要顯式定義,務必在類內定義(其他的運算符重載是可以在類外實現,成員變量訪問沖突也可以通過友元函數解決),因為如果在類外面定義,即使將類成員變量設置成公有,此時類內因為沒有顯示定義,會默認生成一個,這樣在函數調用時會發生沖突,所以不行。編譯器默認生成的賦值重載函數與拷貝構造函數有些類似,都是按存儲空間逐字節拷貝,因為兩者本質都是拷貝,但拷貝構造是先初始化再拷貝,賦值運算符重載是直接拷貝。既然是拷貝,當然就要注意申請空間的情況,務必顯式定義處理這種情況。當我們看見類和類之間用=相連也別一棒子打死說就是賦值運算符重載,也可以是拷貝構造的。

	jiunian x(1, 1);jiunian y(x);//拷貝構造jiunian z = x;//拷貝構造jiunian a(1,1);jiunian b(2, 2);x = y;//賦值運算符重載

要看清是初始化還是賦值。

取地址及const取地址操作符重載

const成員函數

bool operator<(jiunian y) const//const加在后面
{if (_a < y._a && _b < y._b){return true;}return false;
}

將const修飾的“成員函數”稱之為const成員函數,const修飾類成員函數,實際修飾該成員函數隱含的this指針,表明在該成員函數中不能對類的任何成員進行修改。關于const成員函數有幾個經典問題:
(1)const對象可以調用非const成員函數嗎?
(2) 非const對象可以調用const成員函數嗎?
(3)const成員函數內可以調用其它的非const成員函數嗎?
(4)非const成員函數內可以調用其它的const成員函數嗎?
對于const,我們只需要明白一個原則:權限可以平移,縮小,但不能放大。const對象意味著不能改變,而非const成員函數可能會改變const對象,所以不能調用。非const對象意味著可以改變,但不變也不會有問題,所以可以調用const成員函數。const成員函數意味著不能改變參數,而非const成員函數可能會改變參數,所以不能。非const成員函數意味著能改變參數,但也可以不改變,所以能。

取地址及const取地址操作符重載

Date* operator&(){return this ;
比特就業課
}const Date* operator&()const{return this ;}

代碼如上,這兩個默認成員函數很少會自己顯式定義,極特別情況才會,比如別人想要取地址,你調皮就不想讓別人取,一般編譯器默認生成的就夠用了。

static成員

class jiunian
{
public:jiunian(){_a = 1;_b = 1;}static int print(){std::cout << "print" << std::endl;}
private:int _a;int _b;static int _c;
};int jiunian::_c = 0;int main()
{jiunian x;x.print();return 0;
}

聲明為static的類成員稱為類的靜態成員,用static修飾的成員變量,稱之為靜態成員變量;用static修飾的
成員函數,稱之為靜態成員函數。靜態成員變量一定要在類外進行初始化。靜態成員變量有以下特性:
(1)靜態成員為所有類對象所共享,不屬于某個具體的對象,存放在靜態區(所以不在類里面定義)
(2)靜態成員變量必須在類外定義,定義時不添加static關鍵字,類中只是聲明(不能給缺省值,因為不走初始化列表)
(3)類靜態成員即可用 類名::靜態成員 或者 對象.靜態成員 來訪問(靜態成員函數可以不通過對象訪問,直接用作用域解析符訪問)
(4)靜態成員函數沒有隱藏的this指針,不能訪問任何非靜態成員(非要訪問也能自己傳)
(5)靜態成員也是類的成員,受public、protected、private 訪問限定符的限制(初始化時可以破一次例)

友元

using namespace std;class jiunian
{friend ostream& operator<<(ostream& _cout, const Date& x);friend istream& operator>>(istream& _cin, Date& x);
public:jiunian(){}private:int _a;int _b;int _c;
};
ostream& operator<<(ostream& _cout, const Date& x)
{_cout << d._a << "-" << d._b << "-" << d._c;return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{_cin >> d._a;_cin >> d._b;_cin >> d._c;return _cin;
}
int main()
{jiunian a;cin >> a;cout << a << endl;return 0;
}

友元為類外的函數和類提供了一種訪問類內部成員的方式,但友元破壞了封裝,增加了耦合度,所以友元不能頻繁使用。

友元函數

友元函數可以直接訪問聲明過的類的私有和保護成員,聲明時記得加關鍵字friend,友元函數定義在類外,不能屬于任何類。關于友元函數應該注意:
(1)友元函數可訪問類的私有和保護成員,但不是類的成員函數
(2)友元函數不能用const修飾(是指放在后面修飾this指針,放在前面修飾返回值還是可以的)
(3)友元函數可以在類定義的任何地方聲明,不受類訪問限定符限制
(4)一個函數可以是多個類的友元函數
(5)友元函數的調用與普通函數的調用原理相同

友元類

和友元函數一樣,友元類可以直接訪問聲明過的類的私有和保護成員,但是有以下幾點需要注意:
(1)友元關系是單向的,不具有交換性。例如A是B的友元,意味著A可以訪問B的私有或者保護成員,但不意味著反過來B是A的友元,B也不能訪問A的私有或者保護成員。
(2)友元關系不能傳遞.例如A是B的友元,C是A的友元,但C不是B的友元。
(3)友元關系也不能繼承。

內部類

class jiunian
{
public:class thx{public:thx():_x(1){b = 1;//不用作用域解析符也不用類類型對象,直接訪問}private:int _x;};
private:int _a;static int _b;
};int jiunian::_b = 0;int main()
{jiunian m;jiunian::thx n;//作用域解析符,thx被包在jiunian的類域中了return 0;
}

一個類定義在另一個類的內部,這個類就叫內部類。內部類是一個獨立的類,不屬于外部類,無法通過外部類類型對象去訪問內部類,外部類類型對象的存儲空間大小的計算也不會加上內部類,外部類對內部類沒有任何訪問權限上的優越。但內部類是外部類的友元類,內部類可以通過外部類的對象參數來訪問外部類中的所有成員,但是外部類不是內部類的友元。內部類有以下幾點要注意:
(1)內部類可以定義在外部類的public、protected、private都是可以的。
(2)注意內部類可以直接訪問外部類中的static成員,不需要外部類的對象/類名。
(3)sizeof(外部類)=外部類,和內部類沒有任何關系。
(4)不能再內部類中定義外部類的類型對象,會報錯說不允許使用不完整的類型,因為外部類的定義還沒有結束,編譯器無法為內部類的外部類類型對象計算大小。
(5)定義內部類類型對象時不要忘記用作用域解析符。

什么是類與對象

類是面向對象編程中對具有相同屬性和行為的一組事物的抽象描述,它定義了數據成員(屬性)和成員函數(行為),是創建對象的模板而對象是類的具體實例,依據類的定義在內存中被創建,擁有類所規定的屬性和行為,且每個對象的屬性值可以不同,能通過調用成員函數來執行特定操作

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/894474.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/894474.shtml
英文地址,請注明出處:http://en.pswp.cn/news/894474.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大模型訓練(5):Zero Redundancy Optimizer(ZeRO零冗余優化器)

0 英文縮寫 Large Language Model&#xff08;LLM&#xff09;大型語言模型Data Parallelism&#xff08;DP&#xff09;數據并行Distributed Data Parallelism&#xff08;DDP&#xff09;分布式數據并行Zero Redundancy Optimizer&#xff08;ZeRO&#xff09;零冗余優化器 …

陸游的《詩人苦學說》:從藻繪到“功夫在詩外”(中英雙語)mastery lies beyond poetry

陸游的《詩人苦學說》&#xff1a;從藻繪到“功夫在詩外” 今天看萬維鋼的《萬萬沒想到》一書&#xff0c;看到陸游的功夫在詩外的句子&#xff0c;特意去查找這首詩的原文。故而有此文。 我國學人還往往過分強調“功夫在詩外”這句陸游的名言&#xff0c;認為提升綜合素質是一…

DeepSeek-R1 低成本訓練的根本原因是?

在人工智能領域&#xff0c;大語言模型&#xff08;LLM&#xff09;正以前所未有的速度發展&#xff0c;驅動著自然語言處理、內容生成、智能客服等眾多應用的革新。然而&#xff0c;高性能的背后往往是高昂的訓練成本&#xff0c;動輒數百萬美元的投入讓許多企業和研究機構望而…

JavaScript面向對象編程:Prototype與Class的對比詳解

JavaScript面向對象編程&#xff1a;Prototype與Class的對比詳解 JavaScript面向對象編程&#xff1a;Prototype與Class的對比詳解引言什么是JavaScript的面向對象編程&#xff1f;什么是Prototype&#xff1f;Prototype的定義Prototype的工作原理示例代碼優點缺點 什么是JavaS…

玉米苗和雜草識別分割數據集labelme格式1997張3類別

數據集格式&#xff1a;labelme格式(不包含mask文件&#xff0c;僅僅包含jpg圖片和對應的json文件) 圖片數量(jpg文件個數)&#xff1a;1997 標注數量(json文件個數)&#xff1a;1997 標注類別數&#xff1a;3 標注類別名稱:["corn","weed","Bean…

詳解CSS `clear` 屬性及其各個選項

詳解CSS clear 屬性及其各個選項 1. clear: left;示例代碼 2. clear: right;示例代碼 3. clear: both;示例代碼 4. clear: none;示例代碼 總結 在CSS布局中&#xff0c;clear 屬性是一個非常重要的工具&#xff0c;特別是在處理浮動元素時。本文將詳細解釋 clear 屬性及其各個選…

猴子吃桃問題

# 猴子吃桃問題&#xff1a;猴子第一天摘下若干個桃子&#xff0c;當即吃了一半&#xff0c;還不癮&#xff0c;有多吃了一個&#xff0c;第二天早上有將剩下的桃子吃掉一半&#xff0c;又多吃了一個。以后每天早上都吃了前一天剩的一半零一個。到第十天早上想再吃時&#xff0…

Streamlit入門

1、Streamlit是什么 Streamlit 是一個用于快速構建數據應用的開源 Python 庫&#xff0c;由 Streamlit 公司開發并維護。它極大地簡化了從數據腳本到交互式 Web 應用的轉化過程&#xff0c;讓開發者無需具備前端開發的專業知識&#xff0c;就能輕松創建出美觀、實用的交互式應…

機器學習算法在網絡安全中的實踐

機器學習算法在網絡安全中的實踐 本文將深入探討機器學習算法在網絡安全領域的應用實踐&#xff0c;包括基本概念、常見算法及其應用案例&#xff0c;從而幫助程序員更好地理解和應用這一領域的技術。"> 序言 網絡安全一直是信息技術領域的重要議題&#xff0c;隨著互聯…

Rust 所有權特性詳解

Rust 所有權特性詳解 Rust 的所有權系統是其內存安全的核心機制之一。通過所有權規則&#xff0c;Rust 在編譯時避免了常見的內存錯誤&#xff08;如空指針、數據競爭等&#xff09;。本文將從堆內存與棧內存、所有權規則、變量作用域、String 類型、內存分配、所有權移動、Cl…

MVS pythonSamples 運行環境配置

1.首先計算機&#xff1a;操作系統Win10_X64 22H2&#xff1b; 2.MVS V4.4.0 3.python3.8.8_64; 安裝時勾選添加path&#xff1b; 最后安裝依賴包&#xff1a;&#xff08;所有必須安裝&#xff09; 圖像處理&#xff1a; mvtec-halcon23050&#xff08;可選&#xff09; p…

java練習(5)

ps:題目來自力扣 給你兩個 非空 的鏈表&#xff0c;表示兩個非負的整數。它們每位數字都是按照 逆序 的方式存儲的&#xff0c;并且每個節點只能存儲 一位 數字。 請你將兩個數相加&#xff0c;并以相同形式返回一個表示和的鏈表。 你可以假設除了數字 0 之外&#xff0c;這…

[EAI-023] FAST,機器人動作專用的Tokenizer,提高VLA模型的能力和訓練效率

Paper Card 論文標題&#xff1a;FAST: Efficient Action Tokenization for Vision-Language-Action Models 論文作者&#xff1a;Karl Pertsch, Kyle Stachowicz, Brian Ichter, Danny Driess, Suraj Nair, Quan Vuong, Oier Mees, Chelsea Finn, Sergey Levine 論文鏈接&…

PHP Composer:高效依賴管理工具詳解

PHP Composer:高效依賴管理工具詳解 引言 在PHP開發領域,依賴管理是項目構建過程中的重要環節。Composer的出現,極大地簡化了PHP項目的依賴管理,使得開發者可以更加高效地構建和維護PHP應用程序。本文將深入探討PHP Composer的使用方法、功能特點以及它在項目開發中的應用…

CodeGPT使用本地部署DeepSeek Coder

目前NV和github都托管了DeepSeek&#xff0c;生成Key后可以很方便的用CodeGPT接入。CodeGPT有三種方式使用AI&#xff0c;分別時Agents&#xff0c;Local LLMs&#xff08;本地部署AI大模型&#xff09;&#xff0c;LLMs Cloud Model&#xff08;云端大模型&#xff0c;從你自己…

黑盒/白盒運維監控

運維監控分為黑盒和白盒 黑盒&#xff1a;不深入代碼&#xff0c;在系統角度看TPS&#xff0c;延遲等指標 白盒&#xff1a;深入代碼分析&#xff0c;通過日志捕捉&#xff0c;以及主動上報告警等來進行監控 黑盒監控&#xff1a; 1. 頁面功能&#xff1a;域名是否可訪問&…

Rust 中的注釋使用指南

Rust 中的注釋使用指南 注釋是代碼中不可或缺的一部分&#xff0c;它幫助開發者理解代碼的邏輯和意圖。Rust 提供了多種注釋方式&#xff0c;包括行注釋、塊注釋和文檔注釋。本文將詳細介紹這些注釋的使用方法&#xff0c;并通過一個示例展示如何在實際代碼中應用注釋。 1. 行…

可被electron等調用的Qt截圖-錄屏工具【源碼開放】

1. 工具功能簡介&#xff1a; (1)、QT5.15.2截圖工具&#xff08;exe&#xff09;可單獨使用或嵌入IM&#xff08;嵌入方法參照&#xff1a;https://gitee.com/lykiao/yfscreenshot_release&#xff09; (2)、支持通過Windows消息通知截圖成功或取消 (3)、支持圓形、矩形、線條…

ubuntu系統入門流程

學習流程 安裝雙系統&#xff08;win11ubuntu隨便啥版本&#xff0c;博客里面下的時候自己選&#xff09; ->了解一下常見的操作系統類-> 了解ubuntu系統常見文件目錄是做什么的- > 了解一些ubuntu常用指令 ->安裝常用的軟件&#xff08;qq、vx&#xff0c;學習的…

STM32單片機學習記錄(2.2)

一、STM32 13.1 - PWR簡介 1. PWR&#xff08;Power Control&#xff09;電源控制 &#xff08;1&#xff09;PWR負責管理STM32內部的電源供電部分&#xff0c;可以實現可編程電壓監測器和低功耗模式的功能&#xff1b; &#xff08;2&#xff09;可編程電壓監測器&#xff08;…