1、基本概念
1.1、注釋
注釋在翻譯階段3會被替換為單個空白字符從程序中移除
1.2、名字與標識符
標識符是一個由數字、下劃線、大小寫字符組成的任意長度序列。有效的標識符首個字符必須是以A-Z、a-z、下劃線開頭,。有效的標識符其他字符可以是0-9、A-Z、a-z、下劃線。
標識符可以用來命名對象、引用、函數、枚舉項、類型、類成員、命名空間、模板、模板特化、形參包(C++11 起)、goto 標號,以及其他實體。
1.3、類型
C++類型系統由以下類型組成:
- 基礎類型
- void
- std::nullptr_t
- 算數類型
- bool
- 字符類型:char、signed char、unsigned char、char16_t、char32_t、wchar_t
- 有符號整數類型:signed char、short int、int、long int、long long int
- 無符號整數類型:
- 浮點數類型:float、double、long double
- 復合類型
- 引用類型:
- 左值引用類型
- 右值引用類型
- 指針類型
- 指向成員的指針(成員指針)類型
- 數組類型
- 函數類型
- 枚舉類型(有/無作用域)
- 類類型(非聯合/聯合體類型)
- 引用類型:
標量類型:標量類型變量一次僅存儲一個值。算術類型、枚舉類型、指針類型、成員指針類型、std::nullptr_t都是標量類型
1.3.1、隱式生存期類型
隱式生存期類型是C++20引入的概念,無需顯式調用構造函數或使用new表達式,對象的生存期(lifetime)可以隱式開始。
普通生存期類型:
class Person {
public:Person(const std::string& name) : name_(name) {std::cout << "構造: " << name_.c_str() << std::endl;}~Person() {std::cout << "析構: " << name_.c_str() << std::endl;}std::string name_;
};int main()
{void *mem = malloc(sizeof(Person));// 未定義行為!必須先構造再使用// static_cast<Person*>(mem)->name_ = "Alice";Person* p = new (mem) Person("Alice"); // 調用構造函數p->~Person(); // 顯式調用析構函數std::free(mem); // 釋放內存
}
隱式生存期類型:
struct Point {int x;int y;
};int main()
{void* memory = std::malloc(sizeof(Point)); // 分配內存// 可以直接使用,無需構造!Point* p = static_cast<Point*>(memory);p->x = 10; // 合法:內存已被視為Point對象p->y = 20;free(memory); // 釋放內存(自動析構,無需顯式調用析構函數)
}
隱式生存期類型允許:直接操作未構造的內存,無需顯式銷毀對象,可直接覆蓋內存。優點是預先分配大塊內存(內存池),按需創建對象。避免頻繁調用構造 / 析構函數,提升內存操作效率。
隱式生存期類型:
- 標量類型
- 結構體 / 類需滿足:無用戶定義的構造函數、析構函數。所有成員和基類都是隱式生存期類型。
1.3.2、靜態類型
靜態類型(Static Typing)是一種編程語言特性,它要求在編譯時明確每個變量、表達式和函數的類型。
1.3.3、動態類型
動態類型(Dynamic Typing)是一個相對概念,主要指程序在運行時確定對象的實際類型,而非編譯時。C++ 作為靜態類型語言,其核心類型系統是靜態的(編譯時確定類型),有以下幾個機制提供了有限的動態類型特性:
- 多態:基類指針指向派生類對象
- RTTI(運行時類型信息)
1.4、對象
1.4.1、對齊
每個對象類型都具有被稱為對齊要求的性質,它是一個非負整數(類型是 std::size_t,總是 2 的冪),可以使用alignof和std::alignment_of查詢類型的對齊要求,也可以使用alignas要求對齊數。
class Person {
public:Person(const std::string& name) : name_(name) {}
private:std::string name_;
};int main()
{cout << alignof(Person) << endl;cout << std::alignment_of<Person>::value << endl;
}
1.4.2、聲明點
聲明點(Point of Declaration)是一個編譯期概念,它指定了標識符(如變量、函數、類等)在代碼中正式生效的位置。
- 對于變量和函數參數:聲明點位于標識符名稱之后,初始化表達式(如果有)之前
#include <iostream>int main(int argc, char **argv) {int x = 1;const int y = 2;{int x = x; // 內部x的作用域在初始化器之前就開始了,所以內部x不能被外部x的值初始化std::cout << "x = " << x << std::endl;int y[y] = {}; // 內部y的作用域在y[y]之后,所以內部的y是一個包含2個int的數組}return 0;
}
- 類和類模板,枚舉的聲明點位于該標識符之后:
struct S : public A{ // S 的作用域從冒號開始}enum E : int // E 的作用域從冒號開始,因此內部可以使用枚舉類型E
{A = sizeof(E)
};
- 類型別名或別名模板聲明的聲明點緊隨該別名代表的類型標識之后
using T = int; // 外部 T 的作用域從分號開始
{using T = T*; // 內部 T 的作用域從分號開始,// 但分號前還在外部 T 的作用域中,// 因此等同于 T = int*
}
- 對于函數:聲明點位于函數名稱之后,形參列表之前
void func(int x) { // func的聲明點在此處// 函數體中可使用func(如遞歸調用)
}
1.4.3、生存期
對象的生存期(Lifetime) 是指對象從創建(存儲被分配且初始化完成)到銷毀(存儲被釋放或重用)的時間段。理解對象生存期對于避免內存泄漏、懸空指針和資源管理至關重要。
生存期:當對象獲取存儲并初始化完成后生存期開始。對象的生存期在以下幾個時刻結束:
- 非類類型:銷毀該對象時
- 類類型:析構函數調用開始時
- 對象占據的存儲被釋放,或者被其他對象重用
#include <iostream>class A {
public:A() {a = 10;}~A(){}void print() {std::cout << "A: a = "<< a << std::endl;}int a;
};class B {
public:int c;B() {c = 1;}
};void func() {A a;a.~A();new (&a) B;a.print();
}int main(int argc, char **argv) {func();std::cout << "test" << std::endl;return 0;
}
在上述代碼func函數里,我們手動調用了a的析構函數,對象a的生命周期結束了,該對象占用的存儲還在,但它已經不再是一個有效的對象,后續的print調用實際上時一個未定義的行為。
棧上的內存回收是由操作系統自動完成的,當對象的生命周期結束時,操作系統并不會立即將該對象所占用的內存標記為可用,而是等到整個棧幀(通常對應一個函數調用)結束時才會回收棧上的內存。因此,在對象的生命周期結束后,其所在的內存仍然存在,只是該內存已經不再屬于一個有效的對象。
所以析構函數調用后,我們還可以重用a的內存。
生存期分類:
- 自動生存期:局部變量(非static),從定義處開始,到離開作用域時結束。
- 靜態生存期:全局變量、static局部變量、static成員,從程序啟動開始,到程序結束結束。
- 動態生存期:通過new/new[]分配的對象,從new成功開始,到delete/delete[]結束。
- 線程局部生存期:thread_local變量,與線程綁定(線程啟動時開始,線程結束時結束)。
訪問生存期外的對象 或者 重用存儲前未結束生存期會導致未定義行為(UB)。
int* p = new int(10);
delete p;
*p = 20; // UB:p指向的對象已銷毀struct S { ~S() {} };
S* s = new S;
new (s) S; // UB:原S的生存期未顯式結束(需先調用析構)
1.5、聲明與定義
聲明(Declaration) 和 定義(Definition) 是兩個核心概念,它們的區別直接影響代碼的編譯和鏈接過程。
1.5.1、聲明
聲明的主要作用是向編譯器介紹某個標識符(如變量、函數、類等)的存在,告知編譯器該標識符的名稱、類型和一些基本屬性,但并不為其分配內存或實現具體的功能。
聲明的用途是解決編譯時的符號引用。
以下情況是聲明
- 變量聲明:用extern關鍵字且不帶初始化器
extern const int a;
- 類定義中的非 inline(C++17 起) 靜態數據成員的聲明
struct S
{int n; // 定義 S::nstatic int i; // 聲明 S::iinline static int x; // 定義 S::x
}; // 定義 S
int S::i; // 定義 S::i
- 函數聲明:僅提供簽名
void foo(int a);
- 類/類型聲明:
class MyClass;
enum Color : int;
- typedef 聲明,using 聲明
typedef S S2; // 聲明但不定義 S2(S 可以是不完整類型)
using S2 = S; // 聲明但不定義 S2(S 可以是不完整類型)
using N::d; // 聲明,引入一個已存在的名稱,所以N::d必須已經被聲明
重要:聲明可多次重復(同一作用域內允許多次聲明),但是聲明必須完全一致
extern int x; // 聲明1
extern double x; // 錯誤:類型不一致
函數聲明可能分散在多個頭文件中,最終在源文件中定義
// utils.h
void helper();// math.h
void helper(); // 重復聲明(合法)// utils.cpp
void helper() {} // 唯一定義
1.5.2、定義
定義是為標識符分配存儲空間或提供完整實現。每個標識符必須有且僅有一個定義(One Definition Rule, ODR)。
定義的用途是解決鏈接時的具體實現。
- 變量定義:
int x = 42; // 定義并初始化x(分配內存)
- 函數定義
void foo(int a) { // 函數定義std::cout << a;
}
- 類定義:完整描述成員和方法。
class MyClass {
public:void method() {}
};