乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • C++ 学习路径
  • 第1章 C++概述与开发环境

    • C++ 语言概述与编译模型
    • 第一个 C++ 程序与规范
    • 开发环境配置
    • 预处理指令详解
  • 第2章 基本语法与类型系统

    • 变量与基本类型
    • 类型转换
    • 枚举类型
    • 引用与指针
    • 数组与 stdarray
    • 字符串与原始字符串字面量
    • const 与 constexpr
    • nullptr 与空指针
    • 类型推导 auto 与 decltype
    • 基于范围的 for 循环
    • 值类别全面解析
    • char16_t 与 char32_t
    • static_assert 编译期断言
  • 第3章 函数与重载

    • 函数声明与定义
    • 函数重载
    • 默认参数与内联函数
    • Lambda 表达式
    • 函数对象与 stdfunction
    • 后置返回类型与 noexcept
  • 第4章 类与对象

    • 类的基本定义
    • 构造函数与析构函数
    • 拷贝控制
    • 移动构造函数与移动赋值
    • 列表初始化与类内初始化器
    • 静态成员与嵌套类
    • 友元
    • =default 与 =delete
  • 第5章 继承与多态

    • 继承基础
    • 虚函数与多态
    • 虚函数表与动态绑定原理
    • 虚析构函数
    • 抽象类与纯虚函数
    • 多重继承与虚继承
    • 继承构造函数
  • 第6章 运算符重载

    • 运算符重载基础
    • 算术与关系运算符重载
    • 赋值与移动运算符重载
    • 特殊运算符重载
  • 第7章 模板与泛型编程

    • 函数模板
    • 类模板
    • 模板特化与偏特化
    • 可变参数模板
    • 别名模板与模板模板参数
    • SFINAE 与类型萃取
    • 依赖名与 typename/template 关键字
  • 第8章 异常处理

    • 异常处理机制
    • noexcept 与异常安全
    • RAII 与异常安全实践
  • 第9章 内存管理与智能指针

    • 动态内存与内存分区
    • RAII 惯用法
    • unique_ptr
    • shared_ptr 与 weak_ptr
    • 内存管理最佳实践
  • 第10章 右值引用与移动语义

    • 右值引用与值类别深度解析
    • std::move 原理与使用
    • 完美转发与 std::forward
    • 移动语义性能对比与最佳实践
  • 第11章 STL容器

    • vector 深度剖析
    • deque 内部机制
    • list 与 forward_list
    • map 与 set 深度解析
    • unordered 容器与哈希原理
    • array 与 tuple
    • 容器适配器
    • 容器选择全景指南
  • 第12章 STL算法与迭代器

    • 迭代器体系全解
    • 非变异算法
    • 变异算法
    • 排序与二分算法
    • Lambda 与算法组合
    • std::random 随机数库
    • 自定义迭代器开发
  • 第13章 IO流与文件

    • 标准 IO 流
    • 格式化输出控制
    • 文件流操作
    • 字符串流
    • std::regex 正则表达式
  • 第14章 并发与多线程

    • thread 基础与线程管理
    • mutex 与 lock_guard
    • unique_lock 与 condition_variable
    • thread_local 线程局部存储
    • atomic 与内存序
    • future 与 async 异步编程
    • std::chrono 时间库
  • 第15章 现代C++新特性

    • 从 C++11 到 C++20 演进路线
    • C++14 关键新特性
    • C++17 关键新特性
    • C++20 核心特性速览
  • 第16章 面试考点与最佳实践

    • C++ 综合最佳实践清单
    • 高频面试题精讲
    • 多线程面试题与实战
    • 内存管理常见陷阱与排查
  • 附录

    • C++ 核心知识点
    • C++ 专业术语

静态成员与嵌套类

定义与作用

静态成员属于类本身而非任何特定对象,所有对象共享同一份静态数据成员:

class Student {
    static int total_count_;  // 静态数据成员:所有 Student 对象共享
public:
    static int getCount();    // 静态成员函数:不需要对象即可调用
};

嵌套类是在另一个类内部定义的类:

class LinkedList {
    struct Node {  // 嵌套类(默认 private)
        int data;
        Node* next;
    };
    Node* head_;
};

核心原理

静态成员的内存模型

嵌套类的访问权限

完整示例

示例一:学生计数系统

场景说明:用静态成员实现自动学生编号和总数统计。

#include <iostream>
#include <string>
#include <vector>

class Student {
public:
    Student(std::string name, double gpa)
        : name_(std::move(name)), id_(next_id_++), gpa_(gpa) {
        total_count_++;
    }

    ~Student() {
        total_count_--;
    }

    // 静态成员函数:不需要对象即可调用
    static int getTotalCount() { return total_count_; }

    // const 成员函数
    std::string getName() const { return name_; }
    int getID() const { return id_; }
    double getGPA() const { return gpa_; }

    void print() const {
        std::cout << "ID: " << id_ << " | " << name_
                  << " | GPA: " << gpa_ << std::endl;
    }

private:
    std::string name_;
    int id_;
    double gpa_;

    static int total_count_;  // 声明
    static int next_id_;      // 声明
};

// 静态数据成员的定义(必须在类外,且只能写一次)
int Student::total_count_ = 0;
int Student::next_id_ = 1001;

int main() {
    std::cout << "===== 静态成员演示 =====" << std::endl;

    std::cout << "初始学生数: " << Student::getTotalCount() << std::endl;

    {
        Student alice("张三", 3.8);
        Student bob("李四", 3.5);
        Student charlie("王五", 4.0);

        std::cout << "当前学生数: " << Student::getTotalCount() << std::endl;
        alice.print();
        bob.print();
        charlie.print();
    }  // 局部对象销毁

    std::cout << "作用域结束后学生数: " << Student::getTotalCount() << std::endl;

    // 静态成员函数可通过类名或对象调用
    Student dave("赵六", 3.2);
    std::cout << "\n通过类名: " << Student::getTotalCount() << std::endl;
    std::cout << "通过对象: " << dave.getTotalCount() << std::endl;

    return 0;
}

预期输出:

===== 静态成员演示 =====
初始学生数: 0
当前学生数: 3
ID: 1001 | 张三 | GPA: 3.8
ID: 1002 | 李四 | GPA: 3.5
ID: 1003 | 王五 | GPA: 4
作用域结束后学生数: 0

通过类名: 1
通过对象: 1

逐段分析:

  • total_count_ 是静态数据成员——声明在类内,定义在类外。所有对象共享
  • 构造 ++total_count_,析构 --total_count_——自动维护对象计数
  • next_id_ 是静态变量,每次构造自增——实现自动编号
  • getTotalCount() 是静态成员函数——通过 Student::getTotalCount() 调用,无需对象
  • 静态数据成员必须在类外定义(int Student::total_count_ = 0;),且整个程序只能定义一次

示例二:嵌套类实现链表

场景说明:用嵌套类封装链表节点的实现细节。

#include <iostream>
#include <stdexcept>

class IntList {
public:
    IntList() : head_(nullptr) {}

    ~IntList() { clear(); }

    // 禁止拷贝(简化实现)
    IntList(const IntList&) = delete;
    IntList& operator=(const IntList&) = delete;

    void push_front(int value) {
        head_ = new Node(value, head_);
        size_++;
    }

    int pop_front() {
        if (!head_) throw std::runtime_error("空链表");
        Node* old = head_;
        int value = old->data;
        head_ = old->next;
        delete old;
        size_--;
        return value;
    }

    size_t size() const { return size_; }

    void print() const {
        std::cout << "[ ";
        for (Node* p = head_; p; p = p->next) {
            std::cout << p->data;
            if (p->next) std::cout << " → ";
        }
        std::cout << " ] (size=" << size_ << ")" << std::endl;
    }

private:
    // 嵌套结构体:实现细节,外部不可见
    struct Node {
        int data;
        Node* next;

        Node(int d, Node* n = nullptr) : data(d), next(n) {}
    };

    void clear() {
        while (head_) {
            Node* temp = head_;
            head_ = head_->next;
            delete temp;
        }
        size_ = 0;
    }

    Node* head_;
    size_t size_ = 0;
};

int main() {
    IntList list;

    list.push_front(30);
    list.push_front(20);
    list.push_front(10);
    list.print();

    std::cout << "弹出: " << list.pop_front() << std::endl;
    list.print();

    list.push_front(100);
    list.print();

    return 0;
}

预期输出:

[ 10 → 20 → 30 ] (size=3)
弹出: 10
[ 20 → 30 ] (size=2)
[ 100 → 20 → 30 ] (size=3)

逐段分析:

  • Node 是 IntList 的私有嵌套结构体——外部代码无法直接使用 IntList::Node
  • 这种封装让用户只看到 IntList 的公共接口,不知晓内部节点结构
  • 嵌套类默认是外部类的成员,受外部类的访问控制约束(private Node)
  • C++11 起嵌套类可以访问外部类的私有成员

示例三:静态局部变量的延迟初始化

#include <iostream>
#include <string>
#include <map>

class ConfigManager {
public:
    // 单例模式的 C++11 优雅实现(线程安全)
    static ConfigManager& getInstance() {
        static ConfigManager instance;  // 静态局部变量:延迟初始化 + 线程安全
        return instance;
    }

    void set(const std::string& key, const std::string& value) {
        config_[key] = value;
    }

    std::string get(const std::string& key) const {
        auto it = config_.find(key);
        return it != config_.end() ? it->second : "";
    }

private:
    ConfigManager() { std::cout << "ConfigManager 初始化" << std::endl; }
    std::map<std::string, std::string> config_;
};

int main() {
    std::cout << "main 开始" << std::endl;

    // 首次调用才初始化
    ConfigManager::getInstance().set("host", "localhost");

    // 后续调用不重新初始化
    ConfigManager::getInstance().set("port", "3306");

    std::cout << "host: " << ConfigManager::getInstance().get("host") << std::endl;
    std::cout << "port: " << ConfigManager::getInstance().get("port") << std::endl;

    return 0;
}

预期输出:

main 开始
ConfigManager 初始化
host: localhost
port: 3306

易错场景与面试考点

易错场景

场景错误表现正确做法
静态数据成员只声明未定义链接错误 undefined reference在 .cpp 中定义 int T::x = 0;
头文件中定义静态数据成员多重定义声明放 .h,定义放 .cpp
静态成员函数访问非静态成员编译错误(没有 this)通过参数传递对象指针
const static 整型成员的特殊初始化C++11 前规则复杂C++11 起可用 constexpr 替代
嵌套类作为模板参数语法复杂注意 typename 和 template 关键字

常见面试问题

  1. 静态成员变量和普通成员变量的区别?——静态成员属于类、所有对象共享、存储在静态区、在类外定义。普通成员属于对象、每个对象独立、存储在对象内存中。

  2. 静态成员函数可以访问非静态成员吗?为什么?——不能。静态成员函数没有 this 指针,不知道操作哪个对象。

  3. 局部静态变量的初始化是线程安全的吗?——C++11 起是。静态局部变量的初始化在线程间被隐式保护,保证只初始化一次。这是实现单例模式的推荐方式。

  4. 嵌套类的访问权限规则?——嵌套类受外部类访问控制的约束。C++11 起嵌套类可以访问外部类的私有成员(之前需要友元声明)。

  5. static 关键字在 C++ 中有哪些含义?——静态存储期(局部静态变量)、内部链接(文件作用域的 static 函数/变量)、类静态成员。三种含义在不同语境中不同。

小结

  • 静态数据成员属于类,所有对象共享,必须在类外定义
  • 静态成员函数无 this 指针,通过 T::func() 调用
  • 静态局部变量(C++11)是线程安全的延迟初始化——推荐的单例实现方式
  • 嵌套类封装实现细节,C++11 起可访问外部类私有成员
  • static 在 C++ 中有三种含义,根据语境区分
上一页
列表初始化与类内初始化器
下一页
友元