###### tags: `cpp` # class declaration、scope 隨便舉幾個栗子 ## 從函數宣告開始([Declaration][decdef]) 先寫一個加法 ```cpp int sum(int a, int b){ return a + b; } ``` 正常來說還需要先宣告,會變成 ```cpp // forward declaration 只需要型態,可省略變數名 int sum(int a, int b); int sum(int, int); //... // 在這裡給出定義(define) int sum(int a, int b){ return a + b; } ``` 這樣就可以拆成sum.h跟sum.cpp來做外部引用,被稱為分離式編譯(separate compilation) ```cpp // sum.h 負責宣告 int sum(int, int); // sum.cpp 負責定義實作內容 #include "sum.h" int sum(int a, int b){ return a + b; } // main.cpp #include "sum.h" int main(){ cout << sum(3, 4); return 0; } /* dir: │ main.cpp │ sum.cpp │ sum.h */ // g++ main.cpp sum.cpp -o main.exe ``` > 補充: > [Separate Compilation][separate] ## 作用域(scope) 當程式架構越來越大後,多少會有變數名稱的衝突,有scope後就可以透過`::`(Scope resolution operator)來指定要使用誰的 而scope`{}`有各種形式,block、function、class等等,這裡以namesapce舉例 ```cpp namespace n1{ int x = 10; } namespace n2{ int x = 20; } int x = 30; // global x int main() { int x = 40; // local x cout << n1::x << "\n"; // 選擇從n1的命名空間存取x cout << n2::x << "\n"; // 選擇n2的空間 cout << ::x << "\n"; // ::沒有選擇表示global scope cout << x << "\n"; // 預設則是local scope return 0; } output: 10 20 30 40 ``` > 補充: > [Scope resolution operator][scope] > [Scope of Variables][Scope of Variables] > [Namespace in C++ | Set 1 (Introduction)][NamespaceGeek] > [c++入门学习篇(1)之::作用域符解析][zhihuScope] > [Scope cppreference][cpprefScope] > [MSDN Scope (C++)][Scope (C++)] ## Class宣告與定義 Class declaration and definition 再來看class的使用,通常的寫法是把宣告與定義放一起 ```cpp class vec { float x, y; vec(float _x, float _y) { x = _x; y = _y; } void print() const{ cout << x << " " << y << "\n"; } }; ``` 當函示越來越多時,會越來越長,導致看得很痛苦,所以就會希望能**將宣告與定義分開** 而分開的方式就是接續前面提到的**宣告**與**作用域** ```cpp // declaration class vec { float x, y; vec(float, float); void print() const; }; //definition vec::vec(float _x, float _y) : x(_x), y(_y) {} void vec::print() const { std::cout << x << " " << y << "\n"; } ``` 而`class name{..}`實際上是一個scope,所以`vec::print()`就是指定說,定義vec這個class中的print(),以此類推 將宣告與定義分開後,可以透過宣告就了解整個整個calss的在做甚麼,一目了然 ### 應用:內部類別(Nested Classes) 內部類別只被外部包裹的類別所見,當某個Slave類別完全只為了一個Master類別存在時,可以將其包裝成內部類別,這樣使用Master類別的人就不需要用知道Slave的存在,達到更完善的封裝 ```cpp class List { public: List(): head(nullptr), tail(nullptr) {} private: class Node { public: int data; Node* next; Node* prev; }; private: Node* head; Node* tail; }; ``` 如果要在class中再宣告一個class,透過`::`就可以明確地定義class中的class 舉例: 給定正n多邊形的頂點數,生成半徑為10的正n邊形,並顯示其座標 ```cpp // declaration // 宣告外部類別 class Polygon { public: Polygon(int); void show(); // 宣告內部類別 private: class Vec { public: Vec(double, double); void print(); private: double x, y; }; double pi = 3.141592; double radius = 10; vector<Vec*> points; }; //definition // 定義內部類別 Polygon::Vec::Vec(double _x, double _y) : x(_x), y(_y) {} void Polygon::Vec::print() { cout << x << " " << y << "\n"; } // 定義外部類別 Polygon::Polygon(int n) { points.resize(n); for (int i = 0; i < n; i++) { double angle = (2 * pi / n) * i; points[i] = new Vec(radius * cos(angle), radius * sin(angle)); } } void Polygon::show() { for (auto p : points) { p->print(); } } ``` 透過包裝,就可簡化使用方式,使用的人也不用去在意實作細節 ```cpp #include "Polygon.h" int main() { // 生成正3邊形,並顯示座標 Polygon tri(3); tri.show(); return 0; } output: 10 0 -5 8.66026 -5.00001 -8.66025 ``` > 補充 > [Why would one use nested classes in C++?][whynested] > [Nested classes, cppreference][nestedclasses] [decdef]: https://docs.microsoft.com/en-us/cpp/cpp/declarations-and-definitions-cpp?view=msvc-170 [separate]: https://hackingcpp.com/cpp/lang/separate_compilation.html "123" [Scope]: https://www.geeksforgeeks.org/scope-resolution-operator-in-c/ [Scope of Variables]: https://www.geeksforgeeks.org/scope-of-variables-in-c/ [NamespaceGeek]: https://www.geeksforgeeks.org/namespace-in-c/ [zhihuScope]: https://zhuanlan.zhihu.com/p/137383328 [cpprefScope]: https://en.cppreference.com/w/cpp/language/scope [Scope (C++)]: https://docs.microsoft.com/en-us/cpp/cpp/scope-visual-cpp?view=msvc-170 [nestedclasses]: https://en.cppreference.com/w/cpp/language/nested_types [whynested]: https://stackoverflow.com/questions/4571355/why-would-one-use-nested-classes-in-c