返回目錄 1 面向對象技術
上一節 1.6 cpp的常見特性
下一節 1.8 繼承ios
pubulic:公有訪問限定符,具備類外交互能力,類內部外部均可使用;private:私有訪問限定符,僅容許本類中函數訪問,不可在類外使用;c++
protected:保護訪問限定符,僅容許本類及本類的派生類訪問。segmentfault
/* publicAndPrivate.cpp 公有私有訪問權限實例 */ #include <iostream> #include <string> class ExampClass { private: std::string priData; public: ExampClass(); // 默認構造函數,不做修改 ~ExampClass(); // 默認析構函數,不做修改 std::string pubData; void addData(std::string priData, std::string pubData); // 添加數據 void displayPriInfo(); // 顯示私有成員 }; ExampClass::ExampClass() { /* 構造函數裏空空如也 */ } ExampClass::~ExampClass() { /* 析構函數裏空空如也 */ } void ExampClass::addData(std::string priData, std::string pubData) { this->priData = priData; this->pubData = pubData; } void ExampClass::displayPriInfo() { std::cout << priData << std::endl; } int main() { ExampClass exobj; exobj.addData("這是私有數據", "這是公有數據"); exobj.displayPriInfo(); // 顯示剛剛添加的數據,咱們能夠用類內部函數訪問公有和私有成員 std::cout << exobj.pubData << std::endl << std::endl; // 顯示類中的公有成員 // exobj.priDate = "我修改了私有數據"; // <-- 這是錯誤的,沒法直接訪問私有訪問權限的成員 exobj.pubData = "我修改了公有數據"; // 這是正確的,咱們能夠直接訪問和修改公有訪問權限的成員 exobj.displayPriInfo(); // 輸出 std::cout << exobj.pubData << std::endl; return 0; }
這是私有數據 這是公有數據 這是私有數據 我修改了公有數據
1.5中,咱們提到了類中的構造函數和析構函數。數組
構造函數會在類實例化時自動運行,默認是不做任何處理,這個函數能夠重載、添加參數等,通常在構造函數中進行一些初始化操做;函數
析構函數則會在對象生命週期結束之後自動運行,沒法重載和添加參數,通常在析構函數中進行清理操做(如釋放內存空間)。this
對象的生命週期能夠參照變量的生命週期,可是指針申請的內存沒法被自動清理,須要咱們手動清理。.net
構造函數的名字是類名,析構函數則在構造函數前加上取反符號'~'。設計
/* ConstructedAndDestrcutor.cpp 構造函數與析構函數實例 */ #include <iostream> #include <cstdlib> #include <ctime> class ConstructedAndDestrcutor { private: int* array; // 指針,用來申請堆空間 int arraySize; // 存儲數組大小 public: ConstructedAndDestrcutor(); // 無參構造函數 ConstructedAndDestrcutor(int arraySize_l, int maxNum_l); // 有參構造函數,初始化 ~ConstructedAndDestrcutor(); // 析構函數,清理垃圾 void initArray(int arraySize_l, int maxNum_l); // 初始化函數 void displayData(); // 顯示全部隨機生成的數據 }; ConstructedAndDestrcutor::ConstructedAndDestrcutor() { array = nullptr; // 初始化爲nullptr } ConstructedAndDestrcutor::ConstructedAndDestrcutor(int arraySize_l, int maxNum_l) { array = nullptr; initArray(arraySize_l, maxNum_l); // 調用初始化函數 } ConstructedAndDestrcutor::~ConstructedAndDestrcutor() { if (array != nullptr) // 若是array不是nullptr,即已經申請空間 { delete[] array; // 釋放array指向的堆內存 array = nullptr; std::cout << "內存已釋放!" << std::endl; } } void ConstructedAndDestrcutor::initArray(int arraySize_l, int maxNum_l) { this->arraySize = arraySize_l; // 存儲數組大小 array = new int[arraySize_l]; // 申請堆空間 srand((unsigned int)time(0)); // 用時間初始化隨機數種子 for (int i = 0; i < arraySize_l; i++) { array[i] = rand()%(maxNum_l + 1); } } void ConstructedAndDestrcutor::displayData() { for (int i = 0; i < arraySize; i++) { std::cout << array[i] << std::ends; } } int main() { int arraySize, maxNum; std::cout << "請輸入隨機生成的數字個數以及最大值,用空白符號或者英文逗號隔開:" << std::endl; std::cin >> arraySize >> maxNum; /* 這裏用一對大括號表示對象的生命週期,由於vscode調用控制檯會自動在主函數return時退出,而析構函數在return前很快完成,所以沒法看到析構函數效果。這裏將對象生命週期放到括號類,能夠獲得析構函數傳達的信息。 */ { ConstructedAndDestrcutor conades(arraySize, maxNum); conades.displayData(); } return 0; }
>> 請輸入隨機生成的數字個數以及最大值,用空白符號或者英文逗號隔開: << 233 795 >> 656 562 125 259 710 264 350 437 152 756 726 22 755 129 404 790 479 513 638 265 180 288 342 116 226 224 794 518 63 261 637 542 693 554 610 395 401 341 499 203 679 708 299 92 627 564 415 486 604 642 179 196 183 471 487 270 582 220 703 19 589 175 16 85 400 737 140 694 410 81 326 552 210 583 274 291 353 728 593 284 607 453 604 725 177 358 773 264 766 442 563 590 184 691 579 509 477 567 182 568 219 84 253 709 245 492 687 211 59 466 710 569 232 15 485 666 377 196 684 547 325 479 100 494 443 441 429 176 776 59 261 617 114 647 554 490 108 573 163 368 286 513 519 723 499 351 695 682 481 330 749 37 217 221 416 642 56 404 170 25 165 317 608 773 396 438 285 40 262 594 685 478 526 770 15 724 115 526 693 372 331 404 675 103 412 248 2 220 641 332 651 395 775 223 175 568 317 340 72 93 188 488 437 785 415 40 634 479 390 428 779 394 180 289 550 718 167 107 315 131 383 71 179 364 440 274 478 591 732 406 657 523 774 內存已釋放!
構造函數 ,是一種特殊的方法。主要用來在建立對象時初始化對象, 即爲對象成員變量賦初始值,總與new運算符一塊兒使用在建立對象的語句中。特別的一個類能夠有多個構造函數 ,可根據其參數個數的不一樣或參數類型的不一樣來區分它們 即構造函數的重載
拷貝構造函數其實就是用對象去實例化一個對象。指針
/* CopyStructed.cpp 拷貝構造函數實例 */ #include <iostream> #include <cstring> class CopyStructed { private: char* name_l; int age; public: CopyStructed(char* name_l, int age); // 構造函數 CopyStructed(const CopyStructed& copstd); // 拷貝構造函數;const表示對象只讀,不容許被修改 ~CopyStructed(); // 析構函數 void display(); }; CopyStructed::CopyStructed(char* name_l, int age) { this->name_l = nullptr; // 指針置空 /* 複製字符串姓名 */ this->name_l = new char[strlen(name_l) + 1]; strcpy(this->name_l, name_l); this->age = age; // 複製年齡 } CopyStructed::CopyStructed(const CopyStructed& copstd) { this->name_l = nullptr; // 指針置空 /* 複製傳入對象的字符串姓名 */ this->name_l = new char[strlen(copstd.name_l) + 1]; // 保險起見,爲'\0'額外申請一個空間 strcpy(this->name_l, copstd.name_l); this->age = copstd.age; // 複製傳入對象的年齡 } CopyStructed::~CopyStructed() { if (this->name_l != nullptr) { std::cout << "正在清理" << this->name_l << std::endl; delete[] this->name_l; // name_l 不爲空時釋放空間 } } void CopyStructed::display() { std::cout << "姓名:" << this->name_l << std::endl; std::cout << "年齡:" << this->age << std::endl; } int main() { /* 這個大括號是爲了讓控制檯顯示析構函數(做用域) */ { CopyStructed XiaoMing("小明", 28); XiaoMing.display(); CopyStructed XiaoMing2(XiaoMing); XiaoMing2.display(); } return 0; }
姓名:小明 年齡:28 姓名:小明 年齡:28 正在清理小明 正在清理小明
值得注意的是,拷貝堆空間的數據時,不能直接修改指針指向堆空間,而是用新對象的指針從新申請空間拷貝數據,不然由於咱們須要在析構函數中設置清理空間的代碼,原來對象的生命週期結束後,其指針指向的空間也會被釋放,新對象的數據就會丟失。code
/* ConstAndStatic.cpp, const和static在類中的應用 */ #include <iostream> #include <string> class ConstAndStatic { private: static int sum_l; // 初始化一個sum_l來存儲生成了多少對象 std::string name; public: ConstAndStatic(); // 默認構造函數 ConstAndStatic(const std::string name); // 傳入參數不容許被修改 ~ConstAndStatic(); std::string getName() const; // const成員函數沒法修改類中成員和調用非const成員函數 void display() const; static void classSum(); // 顯示生成了多少個對象 }; int ConstAndStatic::sum_l = 0; // 必須在類外初始化類中的static成員,初始化時不須要加上static修飾 ConstAndStatic::ConstAndStatic() { this->name = "默認姓名233"; ConstAndStatic::sum_l++; // 生成對象數+1 } ConstAndStatic::ConstAndStatic(const std::string name) { this->name = name; // 傳入const的姓名 ConstAndStatic::sum_l++; // 生成對象數+1 } ConstAndStatic::~ConstAndStatic() { ConstAndStatic::sum_l--; // 生成對象數-1 } std::string ConstAndStatic::getName() const { return this->name; // 經常用來向類外複製一些數據。 } void ConstAndStatic::display() const { std::cout << "姓名:" << this->getName() << std::endl; // 非const成員函數能夠調用const成員函數 } void ConstAndStatic::classSum() { std::cout << "當前有:" << ConstAndStatic::sum_l << "個對象被生成" << std::endl; // static成員能夠直接在類外經過類名訪問。它們獨立於類,不與對象直接關聯。 } int main() { ConstAndStatic::classSum(); // 顯示當前該類實例化的對象數 { ConstAndStatic castic("張三"); ConstAndStatic::classSum(); // 顯示當前該類實例化的對象數 { ConstAndStatic castics[100]; // 實例化100個對象 ConstAndStatic::classSum(); // 顯示當前該類實例化的對象數 } castic.display(); // 顯示對象信息 ConstAndStatic::classSum(); // 顯示當前該類實例化的對象數 } ConstAndStatic::classSum(); // 顯示當前該類實例化的對象數 return 0; }
當前有:0個對象被生成 當前有:1個對象被生成 當前有:101個對象被生成 姓名:張三 當前有:1個對象被生成 當前有:0個對象被生成
const用來保護類中的成員不被修改;
static則是能夠不用建立對象也能調用的類中內容。
這是比較常見的用法,更多用處請自行探索~
內聯函數能夠再必定程度上代替宏,效果是省去調用函數的開銷,而是直接將函數生成到目標代碼裏。這是一種用空間換時間的方式。
使用方式很簡單,在函數前加上inline標識便可。
內聯函數不能有循環、開關等複雜語句,不然會被當成普通函數。
值得注意的是,類中成員函數一般會被默認定義爲內聯函數。
在類中聲明同時定義的成員函數,自動轉化爲內聯函數。
若是類中成員函數在類內部實現,而且沒有循環、開關等複雜語句,就會默認當成內聯函數;
若是類中成員在類外實現而且沒有inline標識,則不會當成內聯函數;
若是類中成員在類中聲明時標識了inline,則會當成內聯函數;
若是類中成員在類中聲明未標識inline,可是類外實現,則會當成內聯函數;
若是類中成員inline標識,可是實如今另一個文件(.h和.cpp),則認爲不是內聯函數。
能夠參考這一篇博客:C++類裏面的哪些成員函數是內聯函數?
友元容許跨類訪問私有成員。
先來友元函數:
/* Friend.cpp 友元函數實例 */ #include <iostream> /* 先定義兩個類,由於後面涉及兩個類互相調用的問題,先聲明好就不會報錯 */ class A; class B; class A { private: public: A(); ~A(); void setNum(B& b, int num); // 用來修改B類的數據 }; A::A() { } A::~A() { } class B { private: int num; // 要被修改的數據 public: B(); ~B(); friend void display(B&); // 聲明是普通函數的友元函數,用來顯示私有信息 friend void A::setNum(B& b, int num); // 聲明是其餘類中的友元函數,用來修改私有數據成員 }; B::B() { num = 0; } B::~B() { } void A::setNum(B& b, int num) // 函數實現,這個放在B類完整定義以後 { b.num = num; } void display(B& b) // 這是一個能夠訪問B類中私有成員的普通函數,也能夠寫成其餘類的成員函數 { std::cout << b.num << std::endl; } int main() { B b; display(b); // 顯示b中數據 A a; a.setNum(b, 2); // 修改b的數據 display(b); // 顯示b中數據 return 0; }
0 2
接下來是友元類:
/* FriendClass.cpp 友元類實例 */ #include <iostream> class A; // 同樣,先定義 class B; class A { private: int num; public: A(); ~A(); friend class B; // 定義B爲友元類,B中的成員均可以訪問A中全部數據。 }; A::A() { this->num = 0; // 初始化爲0 } A::~A() { } class B { private: public: B(); ~B(); void changeNum(A&, int num); // 在B中修改A的數據 void display(A&); // 在B中顯示A的數據 }; B::B() { } B::~B() { } void B::changeNum(A& a, int num) { a.num = num; // 更改值 } void B::display(A& a) { std::cout << a.num << std::endl; // 輸出值 } int main() { A a; // 實例化A B b; // 實例化B b.display(a); // B顯示A b.changeNum(a, 3); // B修改A的值爲3 b.display(a); // B顯示A return 0; }
0 3
更多特性請自行探索~
返回目錄 1 面向對象技術
上一節 1.6 cpp的常見特性
下一節 1.8 繼承
參考資料: