b站Cherno的課[66]-[70]
- 一、C++的類型雙關
- 二、C++的union(聯合體、共用體)
- 三、C++的虛析構函數
- 四、C++的類型轉換
- 五、條件與操作斷點——VisualStudio小技巧
一、C++的類型雙關
作用:在C++中繞過類型系統
C++是強類型語言
有一個類型系統,不是所有東西都用auto去聲明
可以用auto,畢竟它也是一個關鍵字
但在JavaScript中,沒有變量類型的概念
在創建變量時,不需要聲明變量類型
當我們接受它作為函數的參數時,沒有真正的類型系統
但C++有一個類型系統
當我們創建變量的時候,必須聲明整數或雙精度數或布爾數或結構體或者類等等
然而,這種類型系統并不像在其他語言中那樣強制,比如java
它們的類型很難繞開,包括C#也是
你雖然也可以繞開類型系統,但要做更多的工作
在C++中,雖然類型是由編譯器強制執行的,但您可以直接訪問內存
這意味著在代碼中一直使用這種類型,比如整數,但實際上,我現在要把這段內存,同樣的內存,當作double類型,或者是class類型等
可以很容易地繞過類型系統
你是否要用,取決于你的實際情況
在某些情況下,您絕對不應該規避類型系統,因為類型系統存在是有原因的
#include <iostream>int main()
{int a = 50;//double value = a; 隱式轉換double value = (double)a; // 顯式轉換std::cout << value << std::endl;std::cin.get();
}
#include <iostream>int main()
{int a = 50;//double value = a; 隱式轉換//double value = (double)a; 顯式轉換double& value = *(double*)&a;value = 0.0;std::cout << value << std::endl;std::cin.get();
}
#include <iostream>struct Entity {int x, y;
};int main()
{Entity e = { 5, 8 };// 回到了原始的內存操作int* position = (int*)&e;int y = *(int*)((char*)&e + 4);std::cout << y << std::endl;std::cin.get();
}
我們可以用不同的方式解析同一段內存,從而得到不同的結果,類型只是我們約定的解析內存的方式
類型雙關:我要把我擁有的這段內存,當作不同類型的內存來對待
我們需要做的只是將該類型作為指針,然后將其轉換為另一個指針
然后如果有必要,還可以對它進行解引用
二、C++的union(聯合體、共用體)
聯合體有點像類類型,或者結構體類型
只不過它一次只能占用一個成員的內存
這意思是說,通常如果我們有一個結構體,我們聲明比如4個浮點數,
我們可以有4乘以4個字節在這個結構體中,總共是16個字節
因為我們有四個成員,而且很明顯
當你不斷向類或結構體中添加更多成員時,其大小會不斷增長
一個聯合體只能有一個成員
如果要聲明四個浮點數 ABCD 聯合體的大小仍然是4個字節,當改變ABCD的值的時候,內存是一樣的
改變a設成5 b的值也是5
你可以像使用結構體或類一樣使用它們
你也可以給它添加靜態函數或者普通函數、方法等等
通常union是匿名使用的,但是匿名union不能含有成員函數
通常被用來做類型雙關,union可讀性更強
#include <iostream>struct Vector2 {float x, y;
};struct Vector4 {float x, y, z, w;
};void PrintVector2(const Vector2& vector)
{std::cout << vector.x << "," << vector.y << std::endl;
}int main()
{struct Union{union{float a;int b;};};Union u;u.a = 2.0f;std::cout << u.a << "," << u.b << std::endl;
}
#include <iostream>struct Vector2 {float x, y;
};struct Vector4 {union{// 匿名的,只是一種數據結構,并沒有添加任何東西struct{float x, y, z, w;};struct{Vector2 a, b;};};
};void PrintVector2(const Vector2& vector)
{std::cout << vector.x << "," << vector.y << std::endl;
}int main()
{Vector4 vector = { 1.0f,2.0f,3.0f,4.0f };//vector.x = 2.0f;PrintVector2(vector.a);PrintVector2(vector.b);vector.z = 500.0f;std::cout << "--------------------------" << std::endl;PrintVector2(vector.a);PrintVector2(vector.b);std::cin.get();
}
union里的成員會共享內存,分配的大小是按最大成員的sizeof, 視頻里有兩個成員,也就是那兩個結構體,改變其中一個另外一個里面對應的也會改變. 如果是這兩個成員是結構體struct{ int a,b} 和 int k , 如果k=2 ; 對應 a也=2 ,b不變; union我覺得在這種情況下很好用,就是用不同的結構表示同樣的數據 ,那么你可以按照獲取和修改他們的方式來定義你的 union結構 很方便
一個聯合體的應用場景:開發弱類型語言。例如js,let a=2; 緊接著寫a=“abc”;變量a在一個時間點只會是一種類型,那就可以定義一個聯合體來表示變量的值。
三、C++的虛析構函數
復習:析構函數~(銷毀對象) 虛函數virtual
析構函數:在銷毀對象時運行,卸載變量,清理使用過的內存,同時適用于棧和堆分配的對象
虛函數:允許我們在子類中重寫方法
虛析構函數:二者結合,對于處理多態問題非常重要
#include <iostream>class Base
{
public:Base() { std::cout << "Base Constructor\n"; }~Base() { std::cout << "Base Destrcctor\n"; }
};class Derived : public Base
{
public:Derived() { std::cout << "Derived Constructor\n"; }~Derived() { std::cout << "Derived Destrcctor\n"; }
};int main()
{Base* base = new Base();delete base;std::cout << "----------------\n" << std::endl;Derived* derived = new Derived();delete derived;std::cin.get();
}
int main()
{Base* base = new Base();delete base;std::cout << "----------------\n" << std::endl;Derived* derived = new Derived();delete derived;std::cout << "----------------\n" << std::endl;Base* poly = new Derived();delete poly;std::cin.get();
}
// 只有基類的析構函數被調用,派生類的的析構函數沒有被調用
// 這樣會導致內存泄露
虛析構函數不是覆寫析構函數,而是加上一個析構函數
如果我把基類析構函數改為虛函數
它實際上會調用兩個(析構函數)
它會先調用派生類析構函數,然后在層次結構中向上,調用基類析構函數
標記為virtual,意味著c++就會知道在層次結構下的這個方法可能被重寫了
一定要確保聲明析構函數是虛函數,如果你允許它有子類的話
!!!如果用基類指針來引用派生類對象,那么基類的析構函數必須是 virtual 的,否則 C++ 只會調用基類的析構函數,不會調用派生類的析構函數。
四、C++的類型轉換
C++是強類型語言,意味著存在一個類型系統,并且類型是強制的
#include <iostream>class Base
{
public:Base() {}~Base() {}
};class Derived : public Base
{
public:Derived() {}~Derived() {}
};class AnotherClass : public Base
{
public:AnotherClass() {}~AnotherClass() {}
};int main()
{ // 隱式轉換//int a = 5;//double value = a;double value = 5.25;//int a = value;//int a = (int)value;double a = value + 5.3;std::cout << a << std::endl;std::cin.get();
}
#include <iostream>class Base
{
public:Base() { }~Base() { }
};class Derived : public Base
{
public:Derived() { }~Derived() { }
};class AnotherClass : public Base
{
public:AnotherClass() {}~AnotherClass() {}
};int main()
{ // 隱式轉換//int a = 5;//double value = a;double value = 5.25;//int a = value;//int a = (int)value;// C語言風格類型轉換double a = (int)value + 5.3;std::cout << a << std::endl;std::cin.get();
}
#include <iostream>class Base
{
public:Base() { }virtual ~Base() { }
};class Derived : public Base
{
public:Derived() { }~Derived() { }
};class AnotherClass : public Base
{
public:AnotherClass() {}~AnotherClass() {}
};int main()
{ Derived* derived = new Derived();Base* base = derived;Derived* ac = dynamic_cast<Derived*>(base);std::cin.get();
}
C++風格 共四種主要的cast 類型轉換操作符
一個是 static_cast,還有reinterpret_cast、dynamic_cast、const_cat
static_cast:靜態類型轉換
reinterpret_cast:把這段內存重新解釋成別的東西
const_cat:移除或添加變量的const限定
dynamic_cast:很好的方法來查看是否轉換成功,與運行時類型信息RTTI(runtime type information)緊密相關
regex正則表達式
五、條件與操作斷點——VisualStudio小技巧
關于條件與操作(conditions and actions)應用在斷點上
操作斷點是允許我們采取某種動作
一般是在碰到斷點時打印一些東西到控制臺
兩種類型的操作斷點:
操作斷點和條件斷點