C++ 基础知识点

发布于:2025-05-16 ⋅ 阅读:(16) ⋅ 点赞:(0)

1、指针和引用的区别

指针:是一个变量,存储的是另一个变量的内存地址,可以被重新赋值指向不同的对象,允许为 nullptr。
指针的特性:
独立变量,存储内存地址
可重新赋值指向其他对象
支持空值(nullptr)
支持算术运算(如 ptr++)
适合动态内存管理(new/delete)
引用:是对

象的别名,必须在创建时初始化,之后不能引用其他对象,且不能为空。
引用的特性:
对象的别名,必须初始化
无法更改引用关系
不支持空引用
语法更简洁安全
常用于函数参数传递和操作符重载

int num = 42;
int* ptr = #  // 指针,存储 num 的地址
int& ref = num;   // 引用,num 的别名

*ptr = 10;        // 通过指针修改 num
ref = 20;         // 通过引用修改 num

典型应用场景对比:

// 指针作为函数参数(可修改实参)
void increment(int* ptr) {
    (*ptr)++;  // 通过指针修改值
}

// 引用作为函数参数(更安全的实参修改)
void incrementRef(int& ref) {
    ref++;  // 通过引用直接修改值
}

// 动态内存分配(必须使用指针)
int* createArray(int size) {
    return new int[size];  // 返回堆内存地址
}

// 引用在操作符重载中的应用
class Vector {
public:
    Vector& operator+=(const Vector& rhs) {
        // 重载+=操作符,返回引用支持链式调用
        return *this;
    }
};

在这里插入图片描述
使用建议:

优先使用引用:当需要简化语法并确保安全性时
使用指针:当需要动态内存管理、多级间接引用或需要空值语义时
函数参数传递:
输入参数:优先使用 const 引用
输出参数:优先使用指针(可明确表明参数会被修改)
可选参数:使用指针(配合 nullptr)

2、const 关键字

用于声明常量,保证变量的值不会被修改。可以修饰变量、函数参数、成员函数等。

1、常量变量

const int MAX_SIZE = 100;     // 编译时常量
const double PI = 3.14159;   // 必须初始化,不可修改

特性:
存储在只读内存区(全局 / 静态常量)
编译器会进行常量折叠优化
建议使用constexpr替代单纯的const(C++11 起)

2、指针与 const 的组合(最容易混淆)

  • 指向常量的指针(顶层 const)
const int* p1;  // 写法1:推荐(强调指向常量)
int const* p2;  // 写法2:等价

*p1 = 99;       // 错误:不能通过指针修改对象
int x = 29;
p1 = &x;        // 正确:指针本身可以修改

典型应用:

void printArray(const int* arr, size_t size) {
    for(size_t i=0; i<size; ++i) {
        std::cout << arr[i] << ' ';  // 只能读取,不能修改数组
    }
}
  • 常量指针(底层 const)
int x = 99;
int* const p3 = &x;  // 指针本身是常量

*p3 = 29;            // 正确:可以修改指向的对象
p3 = nullptr;        // 错误:指针本身不可修改
  • 指向常量的常量指针

```cpp在这里插入代码片
const int y = 20;
const int* const p4 = &y; // 指针和指向的对象都不可变

*p4 = 30; // 错误
p4 = &x; // 错误

3、引用与 const

- 常量引用

```cpp
int value = 100;
const int& ref = value;  // 引用一个常量对象

ref = 200;              // 错误:不能通过引用修改
value = 200;            // 正确:原值可以被修改

重要应用场景:

void processString(const std::string& str) {
    // 通过常量引用避免拷贝,同时保证不修改原字符串
    std::cout << "String length: " << str.length();
}

4、const 成员函数

class Vector2D {
private:
    double x, y;
public:
    double getX() const {      // 常量成员函数
        return x;              // 只能读取成员变量
    }
    
    void scale(double factor) {  // 非常量成员函数
        x *= factor;
        y *= factor;
    }
};

const Vector2D v(1.0, 2.0);
v.getX();    // 正确:调用常量成员函数
v.scale(2.0); // 错误:常量对象不能调用非常量函数

底层实现:
常量成员函数的 this 指针类型为const ClassName*
通过mutable关键字可以突破限制:

class Cache {
public:
    int getValue() const {
        if(!cached) {        // mutable变量可以在const函数中修改
            value = computeValue();
            cached = true;
        }
        return value;
    }
private:
    mutable bool cached = false;
    int value;
};

5、const 对象

const MyClass obj;  // 整个对象为常量

obj.func();         // 只能调用const成员函数
obj.member = 10;    // 错误:不能修改成员变量(除非mutable)

6、函数参数中的 const

// 1. 常量值参数(无实际意义)
void func1(const int x) {  // 等价于void func1(int x)
    // x = 10;  // 函数内部不能修改x,但外部传入的值可以是变量
}

// 2. 常量指针参数
void func2(const int* ptr) {
    // *ptr = 10;  // 错误:不能通过指针修改对象
}

// 3. 常量引用参数(最常用)
void func3(const std::vector<int>& vec) {
    // vec.push_back(10);  // 错误:不能修改引用的对象
}

7、const 与重载

class StringView {
public:
    char& operator[](size_t pos) {          // 非常量版本
        return data[pos];
    }
    
    const char& operator[](size_t pos) const {  // 常量版本
        return data[pos];
    }
private:
    char* data;
};

StringView sv;
sv[0] = 'A';          // 调用非常量版本

const StringView csv;
char c = csv[0];      // 调用常量版本

8、使用建议

  • 优先使用 const 引用传递参数:
// 推荐:避免拷贝且保证安全性
void process(const std::string& str);
  • 所有不修改对象的成员函数都应声明为 const:
size_t size() const;  // 明确表示不修改对象状态
  • 使用 const_cast 需极度谨慎:
const int x = 10;
int& rx = const_cast<int&>(x);
rx = 20;  // 未定义行为!
  • 现代 C++ 替代方案:
    使用std::as_const将对象转为常量引用
    使用constexpr替代部分 const 场景

3、static关键字

全局 / 文件作用域:限制变量或函数的作用域为当前文件。
局部变量:延长变量的生命周期,存储在静态存储区。
类成员:属于类而非对象,所有对象共享同一个静态成员。
1、文件作用域静态变量 / 函数(内部链接性)

// file1.cpp
static int fileLocalVar = 10;  // 仅在当前文件可见

static void fileLocalFunc() {  // 仅在当前文件可调用
    // ...
}

特性:
存储在全局数据区
链接属性为内部(Internal Linkage)
避免命名冲突(替代匿名命名空间)

int globalVar = 20;  // 外部链接性,其他文件可用extern访问

2、静态局部变量(延长生命周期)

void counter() {
    static int callCount = 0;  // 首次调用时初始化,后续调用保留值
    callCount++;
    std::cout << "Called " << callCount << " times\n";
}

// 调用示例
counter();  // 输出: Called 1 times
counter();  // 输出: Called 2 times

底层实现:
编译时分配内存(全局数据区)
首次执行到声明处初始化(线程安全,C++11 起)
等价于:

if(!__callCount_initialized) {
    callCount = 0;
    __callCount_initialized = true;
}

3、静态类成员变量(所有对象共享)

class Logger {
public:
    static int logLevel;  // 声明静态成员变量
    static void setLevel(int level) { logLevel = level; }
};

// 必须在类外定义并初始化
int Logger::logLevel = 2;  // 默认级别为2

// 使用示例
Logger::setLevel(3);  // 直接通过类名访问

特性:
全局唯一实例(无论创建多少对象)
必须在类外定义(分配内存)
可被类的所有对象共享访问

访问控制:

class Secret {
private:
    static int privateKey;  // 私有静态成员
};

int Secret::privateKey = 12345;  // 即使是私有成员,也需在类外定义

4、静态类成员函数(无 this 指针)

class MathUtils {
public:
    static double PI;
    
    static double circleArea(double radius) {  // 静态成员函数
        return PI * radius * radius;
    }
    
    void printArea(double r) {  // 非静态成员函数
        std::cout << circleArea(r);  // 可以直接调用静态函数
    }
};

// 使用示例
double area = MathUtils::circleArea(5.0);  // 无需创建对象

限制:
不能访问非静态成员变量 / 函数(无 this 指针)
可以访问其他静态成员
典型应用:
工具类函数(如上面的 MathUtils)
工厂方法(创建对象):

class Widget {
public:
    static Widget* create(int type) {
        if(type == 1) return new Button();
        if(type == 2) return new Label();
        return nullptr;
    }
};

5、静态与面向对象设计

  • 单例模式实现
class Singleton {
private:
    static Singleton* instance;  // 静态实例指针
    Singleton() = default;       // 私有构造函数
public:
    static Singleton* getInstance() {
        if(!instance) instance = new Singleton();
        return instance;
    }
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;
  • 统计对象数量
class ObjectCounter {
private:
    static int count;  // 统计对象总数
public:
    ObjectCounter() { count++; }
    ~ObjectCounter() { count--; }
    
    static int getCount() { return count; }
};

int ObjectCounter::count = 0;  // 初始化为0

6、静态与多线程

静态局部变量初始化:

void initResource() {
    static Resource res = createResource();  // C++11后线程安全
    // ...
}

静态成员变量访问:

class ThreadSafeCounter {
public:
    static void increment() {
        std::lock_guard<std::mutex> lock(mtx);
        count++;
    }
private:
    static int count;
    static std::mutex mtx;  // 静态互斥锁
};

7、常见误区与注意事项
静态成员变量未定义:

class Config {
public:
    static std::string appName;  // 仅声明
};

// 错误:未定义appName就使用
std::cout << Config::appName;  // 链接错误

静态函数访问非静态成员:

class Error {
    int value;
public:
    static void setValue(int v) {
        value = v;  // 错误:静态函数不能访问非静态成员
    }
};

静态对象的析构顺序:
全局静态对象在程序结束时按构造的逆序析构
不同编译单元的静态对象析构顺序不确定(需避免依赖)

8、C++17 新特性:内联静态成员

class Settings {
public:
    inline static int timeout = 3000;  // C++17起无需类外定义
};

等价于:

// 传统写法
class Settings {
public:
    static int timeout;
};
int Settings::timeout = 3000;  // 仍需类外定义

合理使用static可以:

控制变量 / 函数的可见性
实现对象间的数据共享
创建全局唯一实例
优化局部变量的生命周期
实现工具类和工厂方法

4、define

预处理指令,用于定义常量或宏函数,在编译前进行文本替换。

#define PI 3.14159        // 定义常量
#define MAX(a, b) ((a) > (b) ? (a) : (b))  // 定义宏函数

double circleArea(double r) {
    return PI * r * r;  // 替换为 3.14159 * r * r
}

5、inline

建议编译器将函数体直接替换函数调用,减少函数调用开销。适用于短小函数。

inline int add(int a, int b) {
    return a + b;
}

int result = add(3, 4);  // 可能被优化为 int result = 3 + 4;

6、constepr

C++11 引入,用于在编译时求值的常量表达式,可用于优化性能。

constexpr int square(int x) {
    return x * x;
}

constexpr int val = square(5);  // 编译时计算为 25

7、voaltile

告诉编译器该变量可能被意外修改(如硬件交互、多线程环境),禁止编译器优化对该变量的读取。

volatile int sensorValue;  // 可能被外部硬件修改
while (sensorValue == 0);  // 每次都从内存读取,不使用缓存值

8、extern

声明变量或函数在其他文件中定义,扩展作用域。常用于分离编译。

// file1.cpp
extern int sharedVar;  // 声明,定义在其他文件
extern void func();    // 声明外部函数

// file2.cpp
int sharedVar = 42;    // 定义
void func() {}         // 定义

9、std::atomic

C++11 引入的原子操作库,提供线程安全的原子类型,避免数据竞争。

#include <atomic>
#include <thread>

std::atomic<int> counter(0);  // 原子计数器

void increment() {
    for (int i = 0; i < 1000; ++i) {
        counter++;  // 原子操作,线程安全
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    t1.join();
    t2.join();
    return 0;
}

网站公告

今日签到

点亮在社区的每一天
去签到