###### 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