菜鳥教程閱讀筆記,有沒有什麼重難點 Since C++ 類 & 對象。html
到目前爲止,咱們已經對 C++ 的類和對象有了基本的瞭解。下面的列表中還列出了其餘一些 C++ 類和對象相關的概念,能夠點擊相應的連接進行學習。ios
概念 | 描述 |
---|---|
類成員函數 | 類的成員函數是指那些把定義和原型寫在類定義內部的函數,就像類定義中的其餘變量同樣。 |
類訪問修飾符 | 類成員能夠被定義爲 public、private 或 protected。默認狀況下是定義爲 private。 |
構造函數 & 析構函數 | 類的構造函數是一種特殊的函數,在建立一個新的對象時調用。類的析構函數也是一種特殊的函數,在刪除所建立的對象時調用。 |
C++ 拷貝構造函數 | 拷貝構造函數,是一種特殊的構造函數,它在建立對象時,是使用同一類中以前建立的對象來初始化新建立的對象。 |
C++ 友元函數 | 友元函數能夠訪問類的 private 和 protected 成員。 |
C++ 內聯函數 | 經過內聯函數,編譯器試圖在調用函數的地方擴展函數體中的代碼。 |
C++ 中的 this 指針 | 每一個對象都有一個特殊的指針 this,它指向對象自己。 |
C++ 中指向類的指針 | 指向類的指針方式如同指向結構的指針。實際上,類能夠當作是一個帶有函數的結構。 |
C++ 類的靜態成員 | 類的數據成員和函數成員均可以被聲明爲靜態的。 |
利用構造函數初始化。編程
咱們幾乎不使用 protected 或 private 繼承,一般使用 public 繼承。當使用不一樣類型的繼承時,遵循如下幾個規則:bash
Goto: C++多繼承中的二義性問題 數據結構
1、兩個父類中都有同名的成員函數:多線程
2、「菱形」問題——路徑二義性函數
Ref: C++:鑽石繼承與虛繼承【比較詳細】post
父類對祖父類的繼承方式改成虛繼承,那麼子類訪問本身從祖父類那裏繼承過來的成員就不會有二義性問題了,也就是將子類對象裏的祖父類對象統一爲一個,繼承的只有一個祖父類對象。性能
#include "iostream" using namespace std; class Grandpa { public: int year_old; }; class Parent_f:virtual public Grandpa {}; class Parent_m:virtual public Grandpa {}; class Son:public Parent_f,public Parent_m {}; int main() { Son son; son.year_old = 10; cout << "Hello world!" << endl; return 0; }
*** C++ 重載運算符和重載函數 - 歸爲其餘目錄裏。學習
1、虛函數
編譯時綁定,因此子類的函數指針過早的連接了父類。(靜態多態,早綁定)
虛函數 是在基類中使用關鍵字 virtual 聲明的函數。在派生類中從新定義基類中定義的虛函數時,會告訴編譯器不要靜態連接到該函數。
咱們想要的是在程序中任意點能夠根據所調用的對象類型來選擇調用的函數,這種操做被稱爲動態連接,或後期綁定。
如此有是如何訪問父類的函數?
// 存儲矩形的地址 shape = &rec; // 調用矩形的求面積函數 area shape->area(); shape->Shape::area(); // 存儲三角形的地址 shape = &tri; // 調用三角形的求面積函數 area shape->area();
2、純虛函數
接口的實現方式。
class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } // pure virtual function virtual int area() = 0; };
只向外界提供關鍵信息,並隱藏其後臺的實現細節;就是純虛函數實現接口的一種表現形式。
依賴於接口和實現分離的編程(設計)技術。
C++中, 虛函數 能夠爲private, 而且能夠被子類覆蓋(由於虛函數表的傳遞),但子類不能調用父類的private虛函數。虛函數的重載性和它聲明的權限無關。
一個成員函數被定義爲private屬性,標誌着其只能被當前類的其餘成員函數(或友元函數)所訪問。
virtual修飾符則強調父類的成員函數能夠在子類中被重寫,由於重寫之時並無與父類發生任何的調用關係,故而重寫是被容許的。
編譯器不檢查虛函數的各種屬性。被virtual修飾的成員函數,不論他們是private、protect或是public的,都會被統一的放置到虛函數表中。對父類進行派生時,子類會繼承到擁有相同偏移地址的虛函數表(相同偏移地址指,各虛函數相對於VPTR指針的偏移),則子類就會被容許對這些虛函數進行重載。且重載時能夠給重載函數定義新的屬性,例如public,其只標誌着該重載函數在該子類中的訪問屬性爲public,和父類的private屬性沒有任何關係!
純虛函數 能夠設計成私有的,不過這樣不容許在本類以外的非友元函數中直接調用它,子類中只有覆蓋這種純虛函數的義務,卻沒有調用它的權利。
這裏的例子用到了「友元」,也必須有。
From: 虛函數用做private會怎樣?
#include <iostream> using std::cout; using std::endl; class Bclass { private: virtual void fun() { cout << "Bclass fun is here!" << endl ; } friend int main(); }; class Dclass : public Bclass { public: void fun() { cout << "Dclass fun is here!" << endl; } }; int main() { Bclass *pObject = new Dclass(); pObject->fun(); // "Dclass fun is here!" return 0; }
class Box { public: // 純虛函數 virtual double getVolume() = 0;
private: double length; // 長度 double breadth; // 寬度 double height; // 高度 };
大綱:"也是醉了,一個.h文件就有這麼多細節問題 "
緣由:
區別:
首先把數據成員按類型分類並分狀況說明:
注意點:
初始化列表的成員初始化順序:
C++ 初始化類成員時,是按照聲明的順序初始化的,而不是按照出如今初始化列表中的順序。
《一個典型的容易犯錯的例子》
class CMyClass { CMyClass(int x, int y); int m_x; // <----這裏的生命順序決定初始化列表的順序! int m_y; }; CMyClass::CMyClass(int x, int y) : m_y(y), m_x(m_y) { }
From: C++常對象與對象成員
From: C++常對象,常變量,常成員函數詳解(含添加內容)【有常見錯誤例子,不錯】
常成員函數特色:
其餘特色:
她的特色:
何時用?
既要使數據能在必定範圍內共享,又要保證它不被任意修改,這時可使用const。
若是既要利用引用提升程序的效率,又要保護傳遞給函數的數據不在函數中被改變,就應使用常引用。
也就是說,一個實際的一小塊內存,經過原變量能夠改變;經過「影子」引用則不能夠。
#ifndef SALES_DATA_H #define SALES_DATA_H #include <iostream>
using namespace std; class Sales_data { public: //construcitors
Sales_data(const string &s): bookName{s} {}
Sales_data(const string &s, unsigned n, double p): bookName{s}, units_sold{n}, revenue{p*n} {}
Sales_data(void): Sales_data{"", 0, 0} {}
Sales_data(istream &); string getBookName() const; Sales_data& combine(const Sales_data &); double avg_price() const;
//---------------------------------------------------------------------- friend istream& read(istream &is, Sales_data &item); friend ostream& print(ostream &os, const Sales_data &item); private: string bookName; unsigned units_sold {0}; double revenue {0.0}; mutable unsigned no_of_times_called {0}; }; #endif // SALES_DATA_H
成員函數等的實現:
#include "sales_data.h"
using namespace std;
// Declare istream& read(istream &is, Sales_data &item); ostream& print(ostream &os, const Sales_data &item); //member functions
Sales_data::Sales_data(istream &is) { read(is, *this); } string Sales_data::getBookName() const { ++no_of_times_called; return bookName; } double Sales_data::avg_price() const { if (units_sold) { return revenue/units_sold; } else { return 0; } } Sales_data& Sales_data::combine(const Sales_data &rhs) { cout << "combine() in." << endl; units_sold += rhs.units_sold; revenue += rhs.revenue; cout << "combine() out." << endl; return *this; } /* * Non-member class related function. * These need to be decleared as a 'friend' to work. */ istream& read(istream &is, Sales_data &item) { cout << "read() in." << endl; double price {0}; is >> item.bookName >> item.units_sold >> price; item.revenue = price * item.units_sold; cout << "read() out." << endl;
// 技巧:當輸入不符合規範,返回false的is. return is; } ostream& print(ostream &os, const Sales_data &item) { cout << "print() in." << endl; os << item.bookName << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price(); cout << "print() out." << endl; return os; }
main.cpp
#include <iostream> #include "sales_data.h"
using namespace std; int main(void) { cout << "hello world." << endl; Sales_data curBook{"Harry Potter"}; if (read(cin, curBook)) { Sales_data newBook{""}; while (read(cin, newBook)) { cout << "While()..." << endl; if (curBook.getBookName() == newBook.getBookName()) { curBook.combine(newBook); } else { print(cout, curBook) << endl; curBook = newBook; } } print(cout, curBook) << endl; } else { cerr << "No data?!" << endl; } return 0; }
Ref: http://c.biancheng.net/view/170.html
C++ 是在C語言的基礎上發展而來的,第一個 C++ 的編譯器其實是將 C++ 程序翻譯成C語言程序,而後再用C語言編譯器進行編譯。
在non-const成員函數中
在const成員函數中,自動被const。
class A{ public: int a; A(){ cout<<"construct"<<endl; } A(A& b){ cout<<"copy construct"<<endl; } A& operator = (const A& x){ cout<<"operator"<<endl; return *this; } }; class B{ public: A a;
// 方案一 // B(A &m){ // a = m; // }
// 方案二 --> 原理是:a(m)在構造階段,而非計算階段執行
B(A &m):a(m) { } };
兩方案的區別是什麼? 前者構造階段解決戰鬥;後者還須要一次計算。
Solution 01: construct copy construct
Solution 02: construct construct operator
(1) 初始化列表還能解決const與&類型的初始化問題:【以前已有提過】
(2) 如下是錯誤的範例,提醒注意 初始化列表中的 順序性:
#include<iostream>
class X { public: X(int val) : j{val}, i{j} { } private: int i; // 應該先初始化這個 int j; // 再初始化這個 };
構造類型,函數調用,函數聲明間的混淆。
看到括號,究竟是「函數「,仍是」參數初始化「?就是解決這個問題。
最本質的一個區別就是默認的訪問控制,體如今兩個方面:
1)默認的繼承訪問權限。struct是public的,class是private的。
2)struct做爲數據結構的實現體,它默認的數據訪問控制是public的,而class做爲對象的實現體,它默認的成員變量訪問控制是private的。
固然還有其餘區別,見連接。
int i ( static_cast<int> (aDouble) )
避免 -Wall -Werror always warn.
-Werror=narrowing
雖然,聲明在了定義裏,但不要忘了 這不等於其餘人引用該函數時的聲明。
Ref: C++ 友元函數
不是類的成員函數,在此,只是類中的一個聲明。因此天然也就沒有"this pointer"。
class Box { double width; public: double length; friend void printWidth( Box box ); void setWidth( double wid ); };
友元類
class CCar { private: int price; friend class CDriver; //聲明 CDriver 爲友元類 };
class CDriver { public: CCar myCar; void ModifyCar() //改裝汽車 { myCar.price += 1000; //因CDriver是CCar的友元類,故此處能夠訪問其私有成員 } };
int main() { return 0; }
(1) 定義
(2) 清空隊列/bashtable等,若是在多線程時可能會涉及到」mutex「,這個變量就have to be "mutable"!
(3) 一碼歸一碼。在類的內部 不必定就是」本身人「。
const意思是「這個函數不修改對象內部狀態」。
爲了保證這一點,編譯器也會主動替你檢查,確保你沒有修改對象成員變量——不然內部狀態就變了。
mutable意思是「這個成員變量不算對象內部狀態」。
好比,你搞了個變量,用來統計某個對象的訪問次數(好比供debug用)。它變成什麼顯然並不影響對象功用,但編譯器並不知道:它仍然會阻止一個聲明爲const的函數修改這個變量。
把這個計數變量聲明爲mutable,編譯器就明白了:這個變量不算對象內部狀態,修改它並不影響const語義,因此就不須要禁止const函數修改它了
Count how many objs are created by this constructor.
構造一次,counter++。
用法:單例
在類定義中僅聲明,在外才賦值。
有必要抑制此種拉風的寫法:
vector: the Constructor That Takes a Size is Explicit
1. inline
定義在類內部的函數是隱式的內聯函數
能夠在類的內部把inline做爲聲明的一部分顯式地聲明成員函數,也能在類外部用inline關鍵字修飾函數的定義;同時聲明是合法的,可是最好只在類外部定義的地方說明inline,這樣可使類更容易理解。
2. const
用const修飾類的成員函數,必須在類內部聲明時和類外部定義時,同時指定const
3. explicit
只能在類內聲明構造函數時指定,類外定義處不該重複
4. static
類內聲明處指定,類外定義處不能重複指定
5. virtual
virtual只能出如今類內部的聲明語句以前而不能用於類外部的函數定義
在C++中,咱們有時能夠將構造函數用做自動類型轉換函數。但這種自動特性並不是老是合乎要求的,有時會致使意外的類型轉換,所以,C++新增了關鍵字explicit,用於關閉這種自動特性。
即被explicit關鍵字修飾的類構造函數,不能進行自動地隱式類型轉換,只能顯式地進行類型轉換。
注意:只有一個參數的構造函數,或者構造函數有n個參數,但有n-1個參數提供了默認值,這樣的狀況才能進行類型轉換。
具體例子可見原連接:)
暫時沒發現太難理解的東西。