C++核心特性精讲:从C语言痛点出发,掌握现代C++编程精髓
引言:为什么需要从C转向C++?
C语言作为结构化编程的典范,自1972年诞生以来,在系统编程、嵌入式开发等领域占据着不可替代的地位。然而,随着软件规模的不断扩大,C语言在大型项目管理、代码安全性、开发效率等方面逐渐暴露出诸多不足:
- 名字冲突问题:缺乏有效的模块隔离机制
- 指针使用复杂:手动管理内存,容易产生内存泄漏和悬空指针
- 函数设计死板:参数传递缺乏灵活性
- 宏定义缺陷:类型不安全,调试困难
- 缺乏数据封装:面向过程的设计难以应对复杂业务逻辑
C++作为C的超集,不仅100%兼容C语言,更引入了面向对象、泛型编程和现代C++特性,为开发者提供了一整套系统性的解决方案。本文将深入剖析C语言的核心痛点,并通过丰富的代码示例展示C++如何优雅地解决这些问题。
一、解决名字冲突:命名空间(namespace)
1.1 C语言的命名冲突痛点
在C语言中,所有的全局标识符(变量、函数)都共享同一个全局命名空间。当项目规模增大时,这种设计会导致严重的问题:
c
// module1.c
int add(int a, int b) {
return a + b;
}
// module2.c
int add(int a, int b, int c) {
return a + b + c;
}
// main.c
int main() {
// 编译错误:重复定义的符号
int result = add(1, 2);
return 0;
}
此外,与标准库的冲突也时常发生:
c
#include <stdlib.h>
// 与标准库函数重名
void* malloc(size_t size) {
// 自定义内存分配器
return custom_alloc(size);
}
int main() {
// 调用哪个malloc?存在歧义
void* p = malloc(100);
return 0;
}
1.2 C++的解决方案:命名空间
C++引入了命名空间的概念,为标识符提供了逻辑上的分组机制,有效避免了名字冲突。
1.2.1 基本命名空间
cpp
#include <iostream>
// 定义数学计算命名空间
namespace Math {
const double PI = 3.141592653589793;
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
class Calculator {
public:
int multiply(int a, int b) {
return a * b;
}
};
}
// 定义图形计算命名空间
namespace Graphics {
// 与Math::add不冲突
void add(int x, int y) {
std::cout << "移动图形到(" << x << ", " << y << ")" << std::endl;
}
class Point {
private:
int x_, y_;
public:
Point(int x, int y) : x_(x), y_(y) {}
void draw() const {
std::cout << "绘制点(" << x_ << ", " << y_ << ")" << std::endl;
}
};
}
int main() {
// 方式1:完全限定名
std::cout << "Math::PI = " << Math::PI << std::endl;
std::cout << "Math::add(5, 3) = " << Math::add(5, 3) << std::endl;
// 方式2:using声明(引入特定标识符)
using Math::Calculator;
Calculator calc;
std::cout << "5 * 3 = " << calc.multiply(5, 3) << std::endl;
// 方式3:using指令(引入整个命名空间)
using namespace Graphics;
add(10, 20); // 调用Graphics::add
// 不同命名空间的同名函数不冲突
Math::add(1.5, 2.5); // 调用Math::add(double, double)
return 0;
}
1.2.2 嵌套命名空间
C++支持命名空间的嵌套,提供了更精细的模块划分:
cpp
#include <iostream>
#include <string>
namespace Company {
namespace Project {
namespace Module {
class Database {
private:
std::string connection_string_;
public:
Database(const std::string& conn_str)
: connection_string_(conn_str) {}
void connect() {
std::cout << "连接到数据库: "
<< connection_string_ << std::endl;
}
};
// C++17引入的简化嵌套语法
namespace Utils {
std::string format(const std::string& str) {
return "[" + str + "]";
}
}
}
}
}
// C++17语法:简化嵌套命名空间
namespace Company::Project::Network {
class Socket {
public:
void send(const std::string& data) {
std::cout << "发送数据: " << data << std::endl;
}
};
}
int main() {
// 访问嵌套命名空间
Company::Project::Module::Database db("localhost:3306");
db.connect();
// 使用简化语法(C++17)
Company::Project::Network::Socket socket;
socket.send("Hello");
return 0;
}
1.2.3 匿名命名空间
匿名命名空间用于定义仅在当前编译单元中可见的标识符:
cpp
// file1.cpp
namespace {
// 仅在file1.cpp中可见
int internal_counter = 0;
void internal_function() {
// 内部实现细节
}
}
// file2.cpp
namespace {
// 与file1.cpp中的internal_counter不冲突
int internal_counter = 100;
}
// 等效于C语言中的static函数/变量,但更安全
1.2.4 命名空间别名
对于过长的命名空间名称,可以创建简短的别名:
cpp
#include <iostream>
namespace VeryLongNamespaceName {
void importantFunction() {
std::cout << "重要功能" << std::endl;
}
}
// 创建别名
namespace VLN = VeryLongNamespaceName;
namespace S = std; // 不推荐,仅作示例
int main() {
VLN::importantFunction(); // 使用别名
// 函数内部的局部别名
namespace Math = VeryLongNamespaceName;
Math::importantFunction();
return 0;
}
1.3 实际工程中的应用
1.3.1 大型项目中的命名空间管理
cpp
// 项目结构示例:
// - core/ (核心功能)
// - math.hpp (数学库)
// - utils.hpp (工具函数)
// - network/ (网络模块)
// - database/ (数据库模块)
// core/math.hpp
#pragma once
namespace MyProject::Core::Math {
class Vector3D {
private:
double x_, y_, z_;
public:
Vector3D(double x, double y, double z)
: x_(x), y_(y), z_(z) {}
// 向量运算
Vector3D operator+(const Vector3D& other) const;
Vector3D operator-(const Vector3D& other) const;
double dot(const Vector3D& other) const;
// 静态工厂方法
static Vector3D zero() { return Vector3D(0, 0, 0); }
static Vector3D one() { return Vector3D(1, 1, 1); }
};
// 数学常量
namespace Constants {
const double E = 2.718281828459045;
const double SQRT2 = 1.414213562373095;
}
// 线性代数函数
namespace LinearAlgebra {
template<typename T, int N>
class Matrix {
// 矩阵实现
};
}
}
// network/socket.hpp
#pragma once
namespace MyProject::Network {
class Socket {
public:
enum class Protocol {
TCP,
UDP,
SSL
};
explicit Socket(Protocol protocol);
virtual ~Socket();
virtual bool connect(const std::string& host, int port) = 0;
virtual void disconnect() = 0;
virtual size_t send(const void* data, size_t size) = 0;
virtual size_t receive(void* buffer, size_t size) = 0;
};
// 工厂函数
std::unique_ptr<Socket> createSocket(Socket::Protocol protocol);
}
// main.cpp
#include "core/math.hpp"
#include "network/socket.hpp"
int main() {
using namespace MyProject;
// 使用数学模块
Core::Math::Vector3D v1(1.0, 2.0, 3.0);
Core::Math::Vector3D v2(4.0, 5.0, 6.0);
auto sum = v1 + v2;
// 使用网络模块
auto socket = Network::createSocket(Network::Socket::Protocol::TCP);
return 0;
}
1.3.2 命名空间的最佳实践
cpp
#include <iostream>
#include <vector>
#include <algorithm>
// 最佳实践示例
namespace BestPractices {
// 1. 避免在头文件中使用using namespace
// 错误做法:
// using namespace std; // 污染全局命名空间
// 正确做法:
// 在头文件中使用完全限定名或局部using声明
// 2. 使用内联命名空间进行版本控制
namespace v1 {
class API {
public:
void doSomething() {
std::cout << "v1 API" << std::endl;
}
};
}
inline namespace v2 { // v2成为默认版本
class API {
public:
void doSomething() {
std::cout << "v2 API (默认)" << std::endl;
}
void newFeature() {
std::cout << "v2新功能" << std::endl;
}
};
}
// 3. 使用ADL(参数依赖查找)
namespace ADLDemo {
class MyString {
std::string data_;
public:
MyString(const char* str) : data_(str) {}
// 友元函数可以在命名空间外定义
friend std::ostream& operator<<(std::ostream& os,
const MyString& str);
};
// ADL会找到这个函数
std::ostream& operator<<(std::ostream& os, const MyString& str) {
return os << "MyString: " << str.data_;
}
}
}
int main() {
// 版本控制示例
BestPractices::API api_default; // 使用v2(默认)
api_default.doSomething();
api_default.newFeature();
// 明确指定版本
BestPractices::v1::API api_v1;
api_v1.doSomething();
// ADL示例
BestPractices::ADLDemo::MyString str("Hello");
std::cout << str << std::endl; // ADL自动找到正确的operator<<
return 0;
}
二、函数传参更灵活:缺省参数
2.1 C语言函数传参的限制
C语言中,函数调用必须提供所有参数,即使某些参数在大多数情况下使用相同的值:
c
#include <stdio.h>
// 打印日志函数
void log_message(const char* message, int level,
const char* file, int line) {
printf("[%d] %s:%d: %s\n", level, file, line, message);
}
int main() {
// 每次调用都要重复相同的参数
log_message("程序启动", 1, __FILE__, __LINE__);
log_message("用户登录", 2, __FILE__, __LINE__);
log_message("数据库错误", 3, __FILE__, __LINE__);
// 无法简化常用参数的传递
return 0;
}
2.2 C++缺省参数详解
2.2.1 基本用法
cpp
#include <iostream>
#include <string>
// 1. 全缺省参数
void print(const std::string& message,
int level = 1, // 默认级别为1
bool timestamp = true, // 默认包含时间戳
const std::string& prefix = "LOG") { // 默认前缀
if (timestamp) {
// 模拟时间戳
std::cout << "[2024-01-01 12:00:00] ";
}
std::cout << "[" << prefix << "] ";
std::cout << "Level " << level << ": ";
std::cout << message << std::endl;
}
// 2. 半缺省参数(从右向左连续)
void connect(const std::string& hostname,
int port = 80, // 默认HTTP端口
int timeout = 30, // 默认超时30秒
bool use_ssl = false) { // 默认不使用SSL
std::cout << "连接到 " << hostname << ":" << port;
std::cout << " (超时: " << timeout << "秒)";
std::cout << " SSL: " << (use_ssl ? "是" : "否") << std::endl;
}
// 3. 带有复杂类型的缺省参数
class LoggerConfig {
public:
std::string format = "[{level}] {message}";
bool color_output = false;
int max_file_size = 1024 * 1024; // 1MB
};
void configure_logger(const LoggerConfig& config = LoggerConfig()) {
std::cout << "日志配置:" << std::endl;
std::cout << " 格式: " << config.format << std::endl;
std::cout << " 彩色输出: " << (config.color_output ? "是" : "否") << std::endl;
std::cout << " 最大文件大小: " << config.max_file_size << " bytes" << std::endl;
}
int main() {
// 使用不同参数组合调用
print("程序启动"); // 使用所有默认值
print("警告信息", 2); // 只指定级别
print("错误信息", 3, false); // 指定级别和时间戳
print("致命错误", 4, true, "ERROR"); // 指定所有参数
std::cout << std::endl;
// 网络连接示例
connect("example.com"); // 仅指定主机名
connect("api.example.com", 443); // 指定主机名和端口
connect("secure.example.com", 8443, 60, true); // 指定所有参数
std::cout << std::endl;
// 使用默认配置对象
configure_logger();
// 自定义配置
LoggerConfig custom_config;
custom_config.format = "{timestamp} [{level}] {file}:{line} {message}";
custom_config.color_output = true;
configure_logger(custom_config);
return 0;
}
2.2.2 缺省参数的声明与定义
在大型项目中,函数的声明通常放在头文件中,定义放在源文件中。缺省参数只能出现在函数声明中:
cpp
// logger.h - 头文件
#pragma once
#include <string>
namespace Logger {
// 声明函数,指定缺省参数
void debug(const std::string& message,
const std::string& file = __FILE__,
int line = __LINE__);
void error(const std::string& message,
const std::string& file = __FILE__,
int line = __LINE__,
bool send_email = false);
}
// logger.cpp - 源文件
#include "logger.h"
#include <iostream>
#include <ctime>
namespace Logger {
// 定义函数时不重复缺省参数
void debug(const std::string& message,
const std::string& file,
int line) {
auto now = std::time(nullptr);
std::cout << "[DEBUG] " << std::ctime(&now);
std::cout << " " << file << ":" << line << std::endl;
std::cout << " " << message << std::endl;
}
void error(const std::string& message,
const std::string& file,
int line,
bool send_email) {
auto now = std::time(nullptr);
std::cout << "[ERROR] " << std::ctime(&now);
std::cout << " " << file << ":" << line << std::endl;
std::cout << " " << message << std::endl;
if (send_email) {
std::cout << " 错误报告已发送到管理员邮箱" << std::endl;
}
}
}
// main.cpp - 使用示例
#include "logger.h"
int main() {
Logger::debug("应用程序启动");
Logger::error("数据库连接失败");
Logger::error("内存分配失败", __FILE__, __LINE__, true);
return 0;
}
2.2.3 缺省参数与函数重载的配合
缺省参数可以与函数重载结合使用,提供更灵活的接口:
cpp
#include <iostream>
#include <string>
#include <vector>
class DataProcessor {
public:
// 重载版本1:处理单个数据
void process(int data,
const std::string& algorithm = "default") {
std::cout << "处理单个整数: " << data;
std::cout << " (算法: " << algorithm << ")" << std::endl;
}
// 重载版本2:处理数组
void process(const std::vector<int>& data,
const std::string& algorithm = "default",
bool normalize = true) {
std::cout << "处理整数数组 (大小: " << data.size() << ")";
std::cout << " (算法: " << algorithm << ")";
std::cout << " (归一化: " << (normalize ? "是" : "否") << ")" << std::endl;
}
// 重载版本3:处理字符串
void process(const std::string& data,
bool case_sensitive = false,
const std::string& encoding = "UTF-8") {
std::cout << "处理字符串: " << data;
std::cout << " (大小写敏感: " << (case_sensitive ? "是" : "否") << ")";
std::cout << " (编码: " << encoding << ")" << std::endl;
}
};
int main() {
DataProcessor processor;
// 使用不同重载和缺省参数
processor.process(42); // 调用版本1
processor.process(100, "advanced"); // 调用版本1
std::vector<int> numbers = {1, 2, 3, 4, 5};
processor.process(numbers); // 调用版本2
processor.process(numbers, "fast", false); // 调用版本2
processor.process("Hello World"); // 调用版本3
processor.process("C++ Programming", true, "ASCII"); // 调用版本3
return 0;
}
2.2.4 缺省参数的陷阱与注意事项
cpp
#include <iostream>
// 陷阱1:缺省参数在编译时确定
int get_default_value() {
static int counter = 0;
return ++counter;
}
void func_with_pitfall(int x = get_default_value()) {
std::cout << "x = " << x << std::endl;
}
// 陷阱2:虚函数的缺省参数
class Base {
public:
virtual void display(int level = 1) const {
std::cout << "Base::display(level=" << level << ")" << std::endl;
}
};
class Derived : public Base {
public:
void display(int level = 2) const override { // 注意:缺省参数不同!
std::cout << "Derived::display(level=" << level << ")" << std::endl;
}
};
// 解决方案:使用函数重载避免缺省参数问题
class SafeBase {
public:
virtual void display() const {
display(1); // 调用带参数的版本
}
virtual void display(int level) const {
std::cout << "SafeBase::display(level=" << level << ")" << std::endl;
}
virtual ~SafeBase() = default;
};
class SafeDerived : public SafeBase {
public:
void display() const override {
display(2); // 调用带参数的版本
}
void display(int level) const override {
std::cout << "SafeDerived::display(level=" << level << ")" << std::endl;
}
};
int main() {
// 陷阱1示例:缺省参数只计算一次
std::cout << "陷阱1示例:" << std::endl;
func_with_pitfall(); // x = 1
func_with_pitfall(); // x = 1(不是2!)
func_with_pitfall(10); // x = 10
std::cout << "\n陷阱2示例:" << std::endl;
// 陷阱2示例:虚函数的缺省参数
Derived derived;
Base* base_ptr = &derived;
derived.display(); // 输出:Derived::display(level=2)
base_ptr->display(); // 输出:Derived::display(level=1) ⚠️
std::cout << "\n解决方案示例:" << std::endl;
// 使用重载的解决方案
SafeDerived safe_derived;
SafeBase* safe_base_ptr = &safe_derived;
safe_derived.display(); // 输出:SafeDerived::display(level=2)
safe_base_ptr->display(); // 输出:SafeDerived::display(level=2) ✓
return 0;
}
2.3 现代C++中的改进:统一初始化与委托构造函数
cpp
#include <iostream>
#include <string>
#include <vector>
class ModernExample {
private:
std::string name_;
int value_;
std::vector<int> data_;
public:
// 委托构造函数:一个构造函数调用另一个
ModernExample() : ModernExample("default", 0) {}
ModernExample(const std::string& name) : ModernExample(name, 100) {}
ModernExample(const std::string& name, int value)
: name_(name), value_(value) {
// 使用统一初始化语法
data_ = {1, 2, 3, 4, 5};
}
// 统一初始化语法
ModernExample(std::initializer_list<int> init_list)
: name_("from_init_list"), value_(static_cast<int>(init_list.size())) {
data_.assign(init_list.begin(), init_list.end());
}
void print() const {
std::cout << "Name: " << name_ << ", Value: " << value_;
std::cout << ", Data: [";
for (size_t i = 0; i < data_.size(); ++i) {
if (i > 0) std::cout << ", ";
std::cout << data_[i];
}
std::cout << "]" << std::endl;
}
};
// 使用统一初始化的函数
void process_data(std::vector<int> data = {1, 2, 3, 4, 5}) {
std::cout << "处理数据: [";
for (size_t i = 0; i < data.size(); ++i) {
if (i > 0) std::cout << ", ";
std::cout << data[i];
}
std::cout << "]" << std::endl;
}
int main() {
// 使用不同方式构造对象
ModernExample obj1; // 使用默认构造函数
ModernExample obj2("test"); // 使用单参数构造函数
ModernExample obj3("custom", 42); // 使用完整构造函数
ModernExample obj4{10, 20, 30, 40}; // 使用初始化列表
obj1.print();
obj2.print();
obj3.print();
obj4.print();
// 函数调用中的统一初始化
process_data(); // 使用默认值
process_data({5, 10, 15}); // 传递初始化列表
return 0;
}
三、变量的"外号":引用(&)
3.1 C语言指针的痛点
c
#include <stdio.h>
#include <stdlib.h>
// C语言中必须使用指针来修改外部变量
void swap_c(int* a, int* b) {
if (!a || !b) { // 必须检查空指针
return;
}
int temp = *a; // 需要解引用
*a = *b;
*b = temp;
}
// 复杂的指针操作容易出错
void process_array_c(int** array, int* size) {
if (!array || !*array || !size) {
return;
}
// 复杂的指针解引用
for (int i = 0; i < *size; i++) {
(*array)[i] *= 2; // 容易写错为 *array[i]
}
}
int main() {
int x = 10, y = 20;
swap_c(&x, &y); // 必须使用取地址操作符
int* arr = malloc(5 * sizeof(int));
int size = 5;
// 复杂的参数传递
process_array_c(&arr, &size);
free(arr);
return 0;
}
3.2 C++引用的基本概念
3.2.1 引用的基本用法
cpp
#include <iostream>
#include <string>
// 1. 基本引用类型
void basic_reference_demo() {
std::cout << "=== 基本引用示例 ===" << std::endl;
int original = 42;
int& ref = original; // 引用必须初始化
std::cout << "original = " << original << std::endl;
std::cout << "ref = " << ref << std::endl;
// 修改引用就是修改原变量
ref = 100;
std::cout << "修改ref后:" << std::endl;
std::cout << "original = " << original << std::endl;
std::cout << "ref = " << ref << std::endl;
// 引用一旦绑定就不能更改绑定对象
int another = 200;
ref = another; // 这不是重新绑定,而是赋值操作
std::cout << "ref = another 后:" << std::endl;
std::cout << "original = " << original << std::endl; // original被修改为200
std::cout << "another = " << another << std::endl; // another仍然是200
}
// 2. 引用作为函数参数(替代指针)
void swap_cpp(int& a, int& b) {
int temp = a; // 无需解引用,语法更简洁
a = b;
b = temp;
}
// 3. 引用作为函数返回值
class LargeObject {
private:
int data_[1000];
public:
int& operator[](size_t index) {
if (index >= 1000) {
static int dummy = 0;
return dummy;
}
return data_[index];
}
const int& operator[](size_t index) const {
if (index >= 1000) {
static const int dummy = 0;
return dummy;
}
return data_[index];
}
// 返回成员变量的引用
int& first() { return data_[0]; }
const int& first() const { return data_[0]; }
};
// 4. 链式调用(返回引用实现)
class Calculator {
private:
double value_;
public:
Calculator(double initial = 0) : value_(initial) {}
Calculator& add(double x) {
value_ += x;
return *this;
}
Calculator& subtract(double x) {
value_ -= x;
return *this;
}
Calculator& multiply(double x) {
value_ *= x;
return *this;
}
Calculator& divide(double x) {
if (x != 0) {
value_ /= x;
}
return *this;
}
double get() const { return value_; }
};
int main() {
basic_reference_demo();
std::cout << "\n=== 引用作为函数参数 ===" << std::endl;
int x = 10, y = 20;
std::cout << "交换前: x=" << x << ", y=" << y << std::endl;
swap_cpp(x, y); // 无需取地址,语法更直观
std::cout << "交换后: x=" << x << ", y=" << y << std::endl;
std::cout << "\n=== 引用作为函数返回值 ===" << std::endl;
LargeObject obj;
// 使用引用返回值可以直接修改对象内部
obj[0] = 42; // operator[]返回引用
obj[1] = 100;
std::cout << "obj[0] = " << obj[0] << std::endl;
std::cout << "obj[1] = " << obj[1] << std::endl;
// 链式调用示例
std::cout << "\n=== 链式调用 ===" << std::endl;
Calculator calc(10);
calc.add(5).multiply(2).subtract(3).divide(4);
std::cout << "计算结果: " << calc.get() << std::endl;
return 0;
}
3.2.2 引用与指针的对比
cpp
#include <iostream>
#include <vector>
void compare_reference_pointer() {
std::cout << "=== 引用与指针对比 ===" << std::endl;
int value = 42;
// 指针:存储地址的变量
int* ptr = &value;
// 引用:变量的别名
int& ref = value;
std::cout << "原始值: " << value << std::endl;
std::cout << "指针值: " << *ptr << std::endl;
std::cout << "引用值: " << ref << std::endl;
// 修改对比
*ptr = 100; // 通过指针修改
std::cout << "通过指针修改后: " << value << std::endl;
ref = 200; // 通过引用修改
std::cout << "通过引用修改后: " << value << std::endl;
// 重新指向对比
int another = 300;
ptr = &another; // 指针可以重新指向
// ref = another; // 这不是重新绑定,而是赋值
// 空值对比
ptr = nullptr; // 指针可以为空
// int& bad_ref; // 错误:引用必须初始化
// int& null_ref = nullptr; // 错误:不能绑定到nullptr
}
// 在实际项目中的应用选择
class DataProcessor {
public:
// 情况1:需要修改参数 - 使用引用
void process_data(std::vector<int>& data) {
for (auto& item : data) {
item *= 2; // 直接修改原数据
}
}
// 情况2:不需要修改但避免拷贝 - 使用const引用
double calculate_average(const std::vector<int>& data) const {
if (data.empty()) return 0.0;
double sum = 0;
for (const auto& item : data) {
sum += item;
}
return sum / data.size();
}
// 情况3:参数可选 - 使用指针(或std::optional)
bool find_value(const std::vector<int>& data, int target, int* position = nullptr) {
for (size_t i = 0; i < data.size(); ++i) {
if (data[i] == target) {
if (position) { // 检查指针是否有效
*position = static_cast<int>(i);
}
return true;
}
}
return false;
}
// 情况4:返回动态分配的对象 - 使用智能指针
std::unique_ptr<std::vector<int>> create_filtered_data(
const std::vector<int>& data,
std::function<bool(int)> predicate) {
auto result = std::make_unique<std::vector<int>>();
for (const auto& item : data) {
if (predicate(item)) {
result->push_back(item);
}
}
return result;
}
};
int main() {
compare_reference_pointer();
std::cout << "\n=== 在实际项目中的应用 ===" << std::endl;
DataProcessor processor;
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 情况1:需要修改数据
std::cout << "原始数据: ";
for (const auto& item : data) std::cout << item << " ";
std::cout << std::endl;
processor.process_data(data);
std::cout << "处理后的数据: ";
for (const auto& item : data) std::cout << item << " ";
std::cout << std::endl;
// 情况2:只读访问
double avg = processor.calculate_average(data);
std::cout << "平均值: " << avg << std::endl;
// 情况3:可选参数
int position = -1;
if (processor.find_value(data, 10, &position)) {
std::cout << "找到10,位置: " << position << std::endl;
}
// 情况4:返回动态对象
auto filtered = processor.create_filtered_data(
data, [](int x) { return x % 2 == 0; });
std::cout << "偶数数据: ";
for (const auto& item : *filtered) std::cout << item << " ";
std::cout << std::endl;
return 0;
}
3.2.3 引用的高级用法
cpp
#include <iostream>
#include <array>
#include <functional>
#include <memory>
// 1. 引用包装器(std::reference_wrapper)
void reference_wrapper_demo() {
std::cout << "=== 引用包装器 ===" << std::endl;
int x = 10, y = 20, z = 30;
// 创建引用包装器
std::reference_wrapper<int> ref_x = x;
std::reference_wrapper<int> ref_y = y;
// 可以存储在容器中
std::vector<std::reference_wrapper<int>> numbers = {ref_x, ref_y};
// 可以通过get()获取原始引用
numbers.push_back(z); // 隐式转换
// 修改
for (auto& ref : numbers) {
ref.get() *= 2;
}
std::cout << "x = " << x << std::endl; // 20
std::cout << "y = " << y << std::endl; // 40
std::cout << "z = " << z << std::endl; // 60
// 使用std::ref和std::cref
auto print_value = [](int& val) { std::cout << val << " "; };
std::for_each(numbers.begin(), numbers.end(),
[&](std::reference_wrapper<int> ref) {
print_value(ref);
});
std::cout << std::endl;
}
// 2. 完美转发中的引用(现代C++重要特性)
template<typename T>
void process_impl(T&& value) {
// 使用std::forward实现完美转发
std::cout << "处理值: " << std::forward<T>(value) << std::endl;
}
template<typename... Args>
void process_all(Args&&... args) {
// 折叠表达式(C++17)
(process_impl(std::forward<Args>(args)), ...);
}
// 3. 引用与移动语义
class Resource {
private:
std::unique_ptr<int[]> data_;
size_t size_;
public:
Resource(size_t size) : size_(size) {
data_ = std::make_unique<int[]>(size);
std::cout << "分配 " << size << " 个整数" << std::endl;
}
// 移动构造函数
Resource(Resource&& other) noexcept
: data_(std::move(other.data_)), size_(other.size_) {
other.size_ = 0;
std::cout << "移动构造" << std::endl;
}
// 移动赋值运算符
Resource& operator=(Resource&& other) noexcept {
if (this != &other) {
data_ = std::move(other.data_);
size_ = other.size_;
other.size_ = 0;
std::cout << "移动赋值" << std::endl;
}
return *this;
}
// 删除拷贝操作
Resource(const Resource&) = delete;
Resource& operator=(const Resource&) = delete;
~Resource() {
if (data_) {
std::cout << "释放资源" << std::endl;
}
}
int& operator[](size_t index) {
return data_[index];
}
size_t size() const { return size_; }
};
// 4. 右值引用(C++11引入)
void rvalue_reference_demo() {
std::cout << "\n=== 右值引用 ===" << std::endl;
int x = 42;
// 左值引用(绑定到左值)
int& lref = x;
// 右值引用(绑定到临时对象)
int&& rref = 100; // 100是右值
// 右值引用可以延长临时对象的生命周期
const std::string& s1 = std::string("临时字符串");
// 移动语义示例
Resource res1(100);
Resource res2 = std::move(res1); // 使用移动构造函数
std::cout << "res1.size() = " << res1.size() << std::endl; // 0
std::cout << "res2.size() = " << res2.size() << std::endl; // 100
}
int main() {
reference_wrapper_demo();
std::cout << "\n=== 完美转发 ===" << std::endl;
int x = 42;
const int y = 100;
// 完美转发保持值类别
process_all(x, y, 200, std::string("Hello"));
rvalue_reference_demo();
return 0;
}
四、更安全的引用:const引用
4.1 const引用的基本概念
cpp
#include <iostream>
#include <string>
#include <vector>
// 1. const引用的基本用法
void const_reference_basics() {
std::cout << "=== const引用基础 ===" << std::endl;
int x = 10;
const int y = 20;
// 普通引用
int& ref_x = x; // OK
// int& ref_y = y; // 错误:不能将普通引用绑定到const变量
// const引用
const int& cref_x = x; // OK:const引用可以绑定到非const变量
const int& cref_y = y; // OK:const引用可以绑定到const变量
// 通过const引用访问
std::cout << "cref_x = " << cref_x << std::endl;
std::cout << "cref_y = " << cref_y << std::endl;
// 不能通过const引用修改原值
// cref_x = 100; // 错误:cref_x是const引用
x = 100; // OK:可以直接修改原变量
std::cout << "修改x后,cref_x = " << cref_x << std::endl;
}
// 2. const引用绑定临时对象
void temporary_object_demo() {
std::cout << "\n=== const引用绑定临时对象 ===" << std::endl;
// const引用可以延长临时对象的生命周期
const std::string& str_ref = std::string("临时字符串");
std::cout << "临时字符串: " << str_ref << std::endl;
// 不同类型转换
double d = 3.14159;
const int& int_ref = d; // 创建临时int对象并绑定
std::cout << "double转int: " << d << " -> " << int_ref << std::endl;
// 表达式结果
int a = 10, b = 20;
const int& sum_ref = a + b; // 绑定到表达式结果的临时对象
std::cout << "a + b = " << sum_ref << std::endl;
}
// 3. const引用作为函数参数
class DataAnalyzer {
public:
// const引用避免拷贝,同时保证不修改数据
double calculate_mean(const std::vector<double>& data) const {
if (data.empty()) return 0.0;
double sum = 0.0;
for (const double& value : data) { // 循环中使用const引用
sum += value;
}
return sum / data.size();
}
// 查找数据中的最大值
const double& find_max(const std::vector<double>& data) const {
if (data.empty()) {
static const double empty_value = 0.0;
return empty_value;
}
const double* max_ptr = &data[0];
for (const double& value : data) {
if (value > *max_ptr) {
max_ptr = &value;
}
}
return *max_ptr; // 返回const引用,避免拷贝
}
// 同时需要可读和可写的版本
double& get_reference(std::vector<double>& data, size_t index) {
return data[index]; // 返回非const引用,允许修改
}
const double& get_reference(const std::vector<double>& data, size_t index) const {
return data[index]; // 返回const引用,只读访问
}
};
// 4. const成员函数
class BankAccount {
private:
std::string owner_;
mutable double balance_; // mutable允许在const函数中修改
public:
BankAccount(const std::string& owner, double initial_balance)
: owner_(owner), balance_(initial_balance) {}
// const成员函数:不修改对象状态
const std::string& get_owner() const {
return owner_;
}
double get_balance() const {
return balance_;
}
// 非const成员函数:允许修改对象状态
void deposit(double amount) {
if (amount > 0) {
balance_ += amount;
}
}
// const函数中修改mutable成员
void audit_trail() const {
// 可以修改mutable成员
static int audit_count = 0;
audit_count++;
std::cout << "第" << audit_count << "次审计" << std::endl;
}
// 重载:const和非const版本
std::string& details() {
std::cout << "非const版本" << std::endl;
return owner_;
}
const std::string& details() const {
std::cout << "const版本" << std::endl;
return owner_;
}
};
int main() {
const_reference_basics();
temporary_object_demo();
std::cout << "\n=== const引用在数据分析中的应用 ===" << std::endl;
DataAnalyzer analyzer;
std::vector<double> data = {1.5, 2.5, 3.5, 4.5, 5.5};
double mean = analyzer.calculate_mean(data);
std::cout << "平均值: " << mean << std::endl;
const double& max_value = analyzer.find_max(data);
std::cout << "最大值: " << max_value << std::endl;
// 使用const版本
const std::vector<double>& const_data = data;
const double& const_ref = analyzer.get_reference(const_data, 0);
std::cout << "const引用: " << const_ref << std::endl;
// 使用非const版本
double& nonconst_ref = analyzer.get_reference(data, 0);
nonconst_ref = 10.0;
std::cout << "修改后: " << data[0] << std::endl;
std::cout << "\n=== const成员函数 ===" << std::endl;
BankAccount account("张三", 1000.0);
const BankAccount& const_account = account;
// const对象只能调用const成员函数
std::cout << "账户所有者: " << const_account.get_owner() << std::endl;
std::cout << "账户余额: " << const_account.get_balance() << std::endl;
// 调用const版本的details
const std::string& const_details = const_account.details();
// 非const对象可以调用所有函数
account.deposit(500.0);
std::cout << "存款后余额: " << account.get_balance() << std::endl;
// 调用非const版本的details
std::string& nonconst_details = account.details();
nonconst_details = "李四";
std::cout << "修改后所有者: " << account.get_owner() << std::endl;
// mutable成员
account.audit_trail();
const_account.audit_trail();
return 0;
}
4.2 const正确性的重要性
cpp
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
// 1. const正确性避免错误
class Configuration {
private:
std::vector<std::string> settings_;
mutable std::size_t access_count_ = 0; // 访问计数器
public:
void add_setting(const std::string& setting) {
settings_.push_back(setting);
}
// 错误的设计:返回非const引用
std::string& get_setting_bad(size_t index) {
return settings_[index]; // 外部可以修改内部数据
}
// 正确的设计:根据constness返回不同引用
const std::string& get_setting_good(size_t index) const {
access_count_++; // mutable,可以在const函数中修改
return settings_[index];
}
std::string& get_setting_good(size_t index) {
return settings_[index];
}
size_t get_access_count() const {
return access_count_;
}
};
// 2. const在STL算法中的应用
void stl_const_demo() {
std::cout << "\n=== const在STL算法中的应用 ===" << std::endl;
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
const std::vector<int>& const_numbers = numbers;
// const容器的迭代器也是const的
auto const_begin = const_numbers.begin();
auto const_end = const_numbers.end();
// 使用const迭代器遍历
std::cout << "const遍历: ";
for (auto it = const_begin; it != const_end; ++it) {
std::cout << *it << " ";
// *it = 0; // 错误:不能通过const迭代器修改值
}
std::cout << std::endl;
// 非const容器的非const迭代器
std::cout << "修改前: ";
for (const auto& num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
// 使用算法修改数据
std::for_each(numbers.begin(), numbers.end(),
[](int& n) { n *= 2; });
std::cout << "修改后: ";
for (const auto& num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
}
// 3. constexpr(C++11引入的编译期常量)
class MathConstants {
public:
// constexpr函数可以在编译期计算
static constexpr double PI = 3.141592653589793;
constexpr static double square(double x) {
return x * x;
}
// constexpr构造函数
constexpr MathConstants(double value) : value_(value) {}
constexpr double get_value() const { return value_; }
private:
double value_;
};
// 4. const指针 vs const引用
void const_pointer_vs_reference() {
std::cout << "\n=== const指针 vs const引用 ===" << std::endl;
int value = 42;
const int const_value = 100;
// const指针
const int* ptr1 = &value; // 指向const int的指针
int const* ptr2 = &value; // 同上,语法不同
int* const ptr3 = &value; // const指针,指向int
const int* const ptr4 = &value; // const指针,指向const int
// const引用
const int& ref1 = value; // const引用
const int& ref2 = const_value; // const引用
// 修改测试
value = 50; // OK
// 通过指针修改
// *ptr1 = 60; // 错误:ptr1指向const int
// *ptr2 = 60; // 错误:ptr2指向const int
*ptr3 = 70; // OK:ptr3是指向int的const指针
// *ptr4 = 80; // 错误:ptr4指向const int
// 修改指针本身
int another = 200;
ptr1 = &another; // OK:ptr1本身不是const
// ptr3 = &another; // 错误:ptr3是const指针
// 引用不能重新绑定
// ref1 = another; // 错误:不能给引用重新赋值
std::cout << "value = " << value << std::endl;
std::cout << "*ptr3 = " << *ptr3 << std::endl;
std::cout << "ref1 = " << ref1 << std::endl;
}
// 5. 实际工程中的const使用
class DatabaseConnection {
private:
mutable std::string last_query_; // 缓存最后一次查询
mutable std::size_t query_count_ = 0;
public:
// const函数中的复杂操作
std::vector<std::string> execute_query(const std::string& query) const {
// 记录查询历史(修改mutable成员)
last_query_ = query;
query_count_++;
// 模拟数据库查询
std::cout << "执行查询: " << query << std::endl;
// 返回查询结果
return {"结果1", "结果2", "结果3"};
}
// 获取最后查询(const版本)
const std::string& get_last_query() const {
return last_query_;
}
// 获取查询计数(const版本)
std::size_t get_query_count() const {
return query_count_;
}
};
int main() {
// 1. Configuration示例
Configuration config;
config.add_setting("timeout=30");
config.add_setting("retry=3");
// 危险:可以直接修改内部数据
config.get_setting_bad(0) = "hacked!";
const Configuration& const_config = config;
// 安全:只能读取,不能修改
std::cout << "设置: " << const_config.get_setting_good(0) << std::endl;
std::cout << "访问次数: " << const_config.get_access_count() << std::endl;
stl_const_demo();
std::cout << "\n=== constexpr示例 ===" << std::endl;
// 编译期计算
constexpr double pi_squared = MathConstants::square(MathConstants::PI);
constexpr MathConstants constants(3.14);
std::cout << "PI = " << MathConstants::PI << std::endl;
std::cout << "PI^2 = " << pi_squared << std::endl;
std::cout << "常量值 = " << constants.get_value() << std::endl;
const_pointer_vs_reference();
std::cout << "\n=== 数据库连接示例 ===" << std::endl;
DatabaseConnection db;
const DatabaseConnection& const_db = db;
auto results = const_db.execute_query("SELECT * FROM users");
std::cout << "查询结果数量: " << results.size() << std::endl;
std::cout << "最后查询: " << const_db.get_last_query() << std::endl;
std::cout << "查询次数: " << const_db.get_query_count() << std::endl;
return 0;
}
五、指针和引用的核心区别
5.1 详细对比分析
cpp
#include <iostream>
#include <memory>
#include <functional>
class DetailedComparison {
public:
static void syntax_and_usage() {
std::cout << "=== 语法和使用方式对比 ===" << std::endl;
int value = 42;
// 指针语法
int* ptr = &value; // 需要取地址
*ptr = 100; // 需要解引用
ptr = nullptr; // 可以置空
// 引用语法
int& ref = value; // 直接绑定,无需取地址
ref = 200; // 直接使用,无需解引用
// ref = nullptr; // 错误:不能绑定到nullptr
std::cout << "value = " << value << std::endl;
std::cout << "*ptr = " << *ptr << std::endl;
std::cout << "ref = " << ref << std::endl;
}
static void memory_and_lifetime() {
std::cout << "\n=== 内存和生命周期对比 ===" << std::endl;
// 指针有自己的内存空间
int x = 10;
int* ptr = &x;
std::cout << "x的地址: " << &x << std::endl;
std::cout << "ptr的值: " << ptr << std::endl;
std::cout << "ptr的地址: " << &ptr << std::endl;
std::cout << "ptr的大小: " << sizeof(ptr) << " bytes" << std::endl;
// 引用不占用额外内存(编译器实现细节)
int& ref = x;
std::cout << "ref的大小: " << sizeof(ref) << " bytes" << std::endl;
// 动态内存管理
int* dynamic_ptr = new int(100);
// int& dynamic_ref = *new int(200); // 危险:内存泄漏
delete dynamic_ptr;
// delete &dynamic_ref; // 语法奇怪,容易出错
}
static void safety_comparison() {
std::cout << "\n=== 安全性对比 ===" << std::endl;
// 空指针问题
int* ptr = nullptr;
if (ptr) { // 必须检查
*ptr = 42;
}
// 引用不能为空(更安全)
int value = 10;
int& ref = value; // 总是有效
// int& bad_ref; // 错误:必须初始化
// 野指针问题
int* wild_ptr; // 未初始化,危险!
// int& wild_ref; // 错误:必须初始化
// 悬空指针/引用
int* dangling_ptr = new int(100);
int& dangling_ref = *dangling_ptr;
delete dangling_ptr; // 释放内存
// 现在dangling_ptr和dangling_ref都无效
// *dangling_ptr = 200; // 未定义行为
// dangling_ref = 300; // 未定义行为
}
static void polymorphism_and_inheritance() {
std::cout << "\n=== 多态性和继承对比 ===" << std::endl;
class Base {
public:
virtual void show() { std::cout << "Base" << std::endl; }
virtual ~Base() = default;
};
class Derived : public Base {
public:
void show() override { std::cout << "Derived" << std::endl; }
};
Derived derived;
// 指针:天然支持多态
Base* ptr = &derived;
ptr->show(); // 输出:Derived
// 引用:也支持多态
Base& ref = derived;
ref.show(); // 输出:Derived
// 但引用不能重新绑定到其他对象
Base base;
// ref = base; // 这是赋值,不是重新绑定
}
static void performance_considerations() {
std::cout << "\n=== 性能考虑 ===" << std::endl;
// 引用通常没有性能开销
// 编译器通常将引用实现为指针,但优化后可能直接使用原始变量
const int size = 1000000;
std::vector<int> data(size, 42);
// 通过引用传递大对象避免拷贝
auto process_by_ref = [](std::vector<int>& vec) {
for (auto& item : vec) {
item *= 2;
}
};
// 通过指针传递
auto process_by_ptr = [](std::vector<int>* vec) {
if (vec) {
for (auto& item : *vec) {
item /= 2;
}
}
};
// 性能基本相同,但引用语法更简洁
process_by_ref(data);
process_by_ptr(&data);
std::cout << "处理完成" << std::endl;
}
static void modern_cpp_features() {
std::cout << "\n=== 现代C++特性 ===" << std::endl;
// 1. 智能指针(替代原始指针)
auto smart_ptr = std::make_unique<int>(42);
std::shared_ptr<int> shared = std::make_shared<int>(100);
// 2. 引用包装器
int x = 10, y = 20;
std::vector<std::reference_wrapper<int>> refs = {x, y};
// 3. 完美转发需要引用折叠
auto forward_example = []<typename T>(T&& arg) {
// 通用引用(引用折叠)
return std::forward<T>(arg);
};
// 4. 结构化绑定(C++17)
std::pair<int, std::string> pair{42, "answer"};
auto& [num, str] = pair; // 引用绑定
std::cout << "num = " << num << ", str = " << str << std::endl;
}
};
// 实际工程中的选择指南
class EngineeringGuidelines {
public:
// 规则1:优先使用引用作为函数参数
void process_data(std::vector<int>& data) { // 需要修改参数
// ...
}
void analyze_data(const std::vector<int>& data) { // 不需要修改
// ...
}
// 规则2:使用指针表示可选参数
bool find_value(const std::vector<int>& data,
int target,
int* found_index = nullptr) {
for (size_t i = 0; i < data.size(); ++i) {
if (data[i] == target) {
if (found_index) {
*found_index = static_cast<int>(i);
}
return true;
}
}
return false;
}
// 规则3:使用智能指针管理动态内存
std::unique_ptr<int[]> create_buffer(size_t size) {
return std::make_unique<int[]>(size);
}
// 规则4:返回引用时需要小心生命周期
const std::string& get_name() const {
static const std::string default_name = "default";
return default_name; // 返回静态变量的引用是安全的
}
// 危险:返回局部变量的引用
const std::string& bad_get_name() const {
std::string local_name = "local";
return local_name; // 错误:返回局部变量的引用
}
// 规则5:在类设计中使用引用成员需要小心
class Observer {
private:
std::function<void()>& callback_; // 引用成员
public:
explicit Observer(std::function<void()>& callback)
: callback_(callback) {} // 必须在初始化列表中初始化
void notify() {
callback_();
}
};
};
int main() {
DetailedComparison::syntax_and_usage();
DetailedComparison::memory_and_lifetime();
DetailedComparison::safety_comparison();
DetailedComparison::polymorphism_and_inheritance();
DetailedComparison::performance_considerations();
DetailedComparison::modern_cpp_features();
std::cout << "\n=== 工程实践示例 ===" << std::endl;
EngineeringGuidelines guidelines;
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 使用引用参数
guidelines.process_data(data);
// 使用可选指针参数
int found_index = -1;
if (guidelines.find_value(data, 5, &found_index)) {
std::cout << "找到5,索引: " << found_index << std::endl;
}
// 使用智能指针
auto buffer = guidelines.create_buffer(100);
std::cout << "创建缓冲区大小: 100" << std::endl;
// 安全返回引用
const std::string& name = guidelines.get_name();
std::cout << "名称: " << name << std::endl;
return 0;
}
5.2 选择指针还是引用:决策树
cpp
#include <iostream>
#include <memory>
#include <optional>
#include <functional>
// 决策辅助类
class ReferenceOrPointer {
public:
// 场景1:函数参数传递
static void function_parameters() {
std::cout << "=== 函数参数传递决策 ===" << std::endl;
// 情况A:需要修改参数且参数不为空 -> 使用引用
void modify_in_place(std::string& str); // 推荐
// 情况B:需要修改参数且参数可能为空 -> 使用指针
bool try_modify(std::string* str); // 推荐
// 情况C:不需要修改但避免拷贝 -> 使用const引用
void read_only(const std::string& str); // 推荐
// 情况D:原始数组 -> 使用指针
void process_array(int* array, size_t size); // 推荐
// 现代C++:考虑使用视图
void process_span(std::span<int> data); // C++20推荐
}
// 场景2:返回值
static void return_values() {
std::cout << "\n=== 返回值决策 ===" << std::endl;
// 情况A:返回内部状态的引用 -> 小心生命周期
class Container {
std::vector<int> data_;
public:
// 返回引用允许修改
int& operator[](size_t index) { return data_[index]; }
// 返回const引用只读访问
const int& operator[](size_t index) const { return data_[index]; }
// 危险:返回局部变量引用
const std::string& bad_method() const {
std::string local = "bad";
return local; // 错误!
}
// 安全:返回成员引用
const std::vector<int>& get_data() const { return data_; }
};
// 情况B:返回动态分配的对象 -> 使用智能指针
std::unique_ptr<int[]> create_array(size_t size) {
return std::make_unique<int[]>(size);
}
// 情况C:可能没有值 -> 使用std::optional
std::optional<int> find_value(const std::vector<int>& data, int target) {
for (int value : data) {
if (value == target) {
return value;
}
}
return std::nullopt;
}
}
// 场景3:类成员
static void class_members() {
std::cout << "\n=== 类成员决策 ===" << std::endl;
// 情况A:拥有关系 -> 使用值或智能指针
class Owner {
std::unique_ptr<int> resource_; // 拥有资源
std::vector<int> data_; // 拥有数据
};
// 情况B:观察关系 -> 使用原始指针或weak_ptr
class Observer {
// Subject* subject_; // 不拥有,观察
// std::weak_ptr<Subject> subject_; // 更好的选择
};
// 情况C:关联关系 -> 使用引用(必须初始化)
class Processor {
std::function<void()>& callback_; // 关联回调
public:
explicit Processor(std::function<void()>& callback)
: callback_(callback) {}
};
// 情况D:可选关联 -> 使用指针
class Configurable {
const Config* config_ = nullptr; // 可选配置
public:
void set_config(const Config* config) {
config_ = config;
}
};
}
// 场景4:多态性
static void polymorphism() {
std::cout << "\n=== 多态性决策 ===" << std::endl;
class Shape {
public:
virtual void draw() const = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
public:
void draw() const override {
std::cout << "绘制圆形" << std::endl;
}
};
class Square : public Shape {
public:
void draw() const override {
std::cout << "绘制方形" << std::endl;
}
};
// 情况A:存储多态对象 -> 使用指针或智能指针
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>());
shapes.push_back(std::make_unique<Square>());
// 情况B:传递多态参数 -> 使用引用
void render_shape(const Shape& shape) {
shape.draw(); // 多态调用
}
// 情况C:返回多态对象 -> 使用智能指针
std::unique_ptr<Shape> create_shape(const std::string& type) {
if (type == "circle") return std::make_unique<Circle>();
if (type == "square") return std::make_unique<Square>();
return nullptr;
}
}
// 场景5:性能关键代码
static void performance_critical() {
std::cout << "\n=== 性能关键代码决策 ===" << std::endl;
// 情况A:小对象传值,大对象传引用
struct Small { int x, y; }; // 传值
struct Large { int data[1000]; }; // 传引用
void process_small(Small s) {} // 推荐:传值
void process_large(const Large& l) {} // 推荐:传引用
// 情况B:内联函数 -> 通常传值
inline int add(int a, int b) { return a + b; }
// 情况C:热点循环 -> 使用引用避免拷贝
void process_vector(std::vector<int>& vec) {
for (int& item : vec) { // 使用引用避免拷贝
item *= 2;
}
}
// 情况D:底层操作 -> 使用指针
void memcpy_impl(void* dest, const void* src, size_t n) {
// 底层内存操作通常使用指针
}
}
};
// 综合示例
class Database {
private:
struct Connection {
int id;
std::string name;
};
std::vector<std::unique_ptr<Connection>> connections_;
mutable std::size_t query_count_ = 0;
public:
// 返回智能指针(拥有关系)
std::unique_ptr<Connection> create_connection(const std::string& name) {
auto conn = std::make_unique<Connection>();
conn->id = static_cast<int>(connections_.size());
conn->name = name;
connections_.push_back(std::move(conn));
return std::make_unique<Connection>(*connections_.back());
}
// 返回引用(内部对象,调用者不拥有)
Connection& get_connection(int id) {
if (id >= 0 && id < static_cast<int>(connections_.size())) {
return *connections_[id];
}
throw std::out_of_range("无效的连接ID");
}
// 返回const引用(只读访问)
const Connection& get_connection(int id) const {
if (id >= 0 && id < static_cast<int>(connections_.size())) {
query_count_++; // 修改mutable成员
return *connections_[id];
}
throw std::out_of_range("无效的连接ID");
}
// 使用指针表示可选返回值
Connection* find_connection(const std::string& name) {
for (auto& conn : connections_) {
if (conn->name == name) {
return conn.get();
}
}
return nullptr; // 使用nullptr表示没找到
}
// 使用引用传递回调
void for_each_connection(std::function<void(const Connection&)> callback) const {
for (const auto& conn : connections_) {
callback(*conn);
}
}
};
int main() {
ReferenceOrPointer::function_parameters();
ReferenceOrPointer::return_values();
ReferenceOrPointer::class_members();
ReferenceOrPointer::polymorphism();
ReferenceOrPointer::performance_critical();
std::cout << "\n=== 数据库综合示例 ===" << std::endl;
Database db;
// 创建连接(返回智能指针)
auto conn1 = db.create_connection("主数据库");
auto conn2 = db.create_connection("备份数据库");
// 获取引用(内部对象)
Database::Connection& ref = db.get_connection(0);
ref.name = "修改后的主数据库";
// 使用指针查找
Database::Connection* ptr = db.find_connection("备份数据库");
if (ptr) {
std::cout << "找到连接: " << ptr->name << std::endl;
}
// 使用引用传递回调
db.for_each_connection([](const Database::Connection& conn) {
std::cout << "连接ID: " << conn.id
<< ", 名称: " << conn.name << std::endl;
});
return 0;
}
六、替代宏函数:inline内联函数
6.1 C语言宏函数的缺陷
cpp
#include <iostream>
#include <cstdlib>
// C语言宏函数的典型问题
void macro_problems_demo() {
std::cout << "=== C语言宏函数的缺陷 ===" << std::endl;
// 问题1:缺乏类型检查
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int x = 5, y = 10;
std::cout << "MAX(5, 10) = " << MAX(x, y) << std::endl;
double d1 = 3.14, d2 = 2.71;
std::cout << "MAX(3.14, 2.71) = " << MAX(d1, d2) << std::endl;
// 危险:不同类型混合
std::cout << "MAX(5, 2.71) = " << MAX(x, d2) << std::endl; // 编译通过,但有警告
// 问题2:运算符优先级问题
#define SQUARE(x) x * x
int a = 5;
std::cout << "SQUARE(5) = " << SQUARE(a) << std::endl; // 25,正确
std::cout << "SQUARE(5+1) = " << SQUARE(a + 1) << std::endl; // 11,错误!
// 展开为:5 + 1 * 5 + 1 = 5 + 5 + 1 = 11
// 问题3:多次求值
#define PRINT_AND_INCREMENT(x) \
std::cout << "值: " << (x) << std::endl; \
(x)++
int counter = 0;
std::cout << "\n多次求值问题:" << std::endl;
std::cout << "MAX(counter++, 10) = " << MAX(counter++, 10) << std::endl;
std::cout << "counter = " << counter << std::endl; // counter被增加了2次!
// 问题4:无法调试
// 宏在预处理阶段展开,调试器中看不到宏函数
// 问题5:作用域问题
#define TEMP_VAR 100
int TEMP_VAR = 200; // 编译错误:重定义
std::cout << "\n";
}
// 复杂的宏带来的问题
void complex_macro_issues() {
std::cout << "=== 复杂宏的问题 ===" << std::endl;
// 复杂的宏难以阅读和维护
#define CREATE_PERSON(name, age) \
Person p##name = {#name, age}; \
register_person(&p##name)
struct Person {
const char* name;
int age;
};
void register_person(Person* p) {
std::cout << "注册: " << p->name << ", " << p->age << "岁" << std::endl;
}
// 使用宏
CREATE_PERSON(Alice, 25);
CREATE_PERSON(Bob, 30);
// 问题:##运算符的滥用
#define GET_FIELD(obj, field) (obj).field_##field
struct Data {
int field_id;
std::string field_name;
};
Data data{42, "test"};
std::cout << "ID: " << GET_FIELD(data, id) << std::endl;
std::cout << "Name: " << GET_FIELD(data, name) << std::endl;
// 但宏不能处理动态字段名
std::cout << "\n";
}
6.2 inline内联函数的解决方案
cpp
#include <iostream>
#include <string>
#include <type_traits>
#include <cmath>
// 1. 基本inline函数
inline int max_int(int a, int b) {
return a > b ? a : b;
}
inline double max_double(double a, double b) {
return a > b ? a : b;
}
// 2. 模板inline函数(类型安全)
template<typename T>
inline T max_template(T a, T b) {
return a > b ? a : b;
}
// 3. 带约束的模板(C++20)
template<typename T>
requires std::is_arithmetic_v<T>
inline T max_constrained(T a, T b) {
return a > b ? a : b;
}
// 4. 类内定义的成员函数默认inline
class MathUtils {
public:
// 类内定义的成员函数默认inline
static int square(int x) {
return x * x;
}
static double square(double x) {
return x * x;
}
// 模板成员函数
template<typename T>
static T cube(T x) {
return x * x * x;
}
};
// 5. 复杂的inline函数示例
class Vector3D {
private:
double x_, y_, z_;
public:
Vector3D(double x = 0, double y = 0, double z = 0)
: x_(x), y_(y), z_(z) {}
// 内联getter/setter
inline double x() const { return x_; }
inline double y() const { return y_; }
inline double z() const { return z_; }
inline void set_x(double x) { x_ = x; }
inline void set_y(double y) { y_ = y; }
inline void set_z(double z) { z_ = z; }
// 内联运算符
inline Vector3D operator+(const Vector3D& other) const {
return Vector3D(x_ + other.x_, y_ + other.y_, z_ + other.z_);
}
inline Vector3D operator-(const Vector3D& other) const {
return Vector3D(x_ - other.x_, y_ - other.y_, z_ - other.z_);
}
inline double dot(const Vector3D& other) const {
return x_ * other.x_ + y_ * other.y_ + z_ * other.z_;
}
inline double length() const {
return std::sqrt(dot(*this));
}
inline Vector3D normalized() const {
double len = length();
if (len > 0) {
return Vector3D(x_ / len, y_ / len, z_ / len);
}
return *this;
}
// 静态内联工厂方法
static inline Vector3D zero() { return Vector3D(0, 0, 0); }
static inline Vector3D one() { return Vector3D(1, 1, 1); }
static inline Vector3D up() { return Vector3D(0, 1, 0); }
static inline Vector3D right() { return Vector3D(1, 0, 0); }
static inline Vector3D forward() { return Vector3D(0, 0, 1); }
};
void inline_function_demo() {
std::cout << "=== inline函数解决方案 ===" << std::endl;
// 基本使用
std::cout << "max_int(5, 10) = " << max_int(5, 10) << std::endl;
std::cout << "max_double(3.14, 2.71) = " << max_double(3.14, 2.71) << std::endl;
// 模板版本(类型安全)
std::cout << "max_template(5, 10) = " << max_template(5, 10) << std::endl;
std::cout << "max_template(3.14, 2.71) = " << max_template(3.14, 2.71) << std::endl;
// 类型检查
// max_template(5, 2.71); // 编译错误:类型不匹配
// 带约束的模板
std::cout << "max_constrained(5, 10) = " << max_constrained(5, 10) << std::endl;
// max_constrained("a", "b"); // 编译错误:不满足约束
// 类内函数
std::cout << "MathUtils::square(5) = " << MathUtils::square(5) << std::endl;
std::cout << "MathUtils::cube(3) = " << MathUtils::cube(3) << std::endl;
// Vector3D示例
Vector3D v1(1, 2, 3);
Vector3D v2(4, 5, 6);
Vector3D sum = v1 + v2;
std::cout << "向量相加: (" << sum.x() << ", " << sum.y() << ", " << sum.z() << ")" << std::endl;
double dot_product = v1.dot(v2);
std::cout << "点积: " << dot_product << std::endl;
std::cout << "v1长度: " << v1.length() << std::endl;
Vector3D normalized = v1.normalized();
std::cout << "归一化: (" << normalized.x() << ", " << normalized.y() << ", " << normalized.z() << ")" << std::endl;
// 使用静态工厂方法
Vector3D zero = Vector3D::zero();
Vector3D up = Vector3D::up();
std::cout << "零向量: (" << zero.x() << ", " << zero.y() << ", " << zero.z() << ")" << std::endl;
std::cout << "上向量: (" << up.x() << ", " << up.y() << ", " << up.z() << ")" << std::endl;
}
// 6. inline函数的性能分析
class PerformanceAnalyzer {
public:
// 适合inline的小函数
inline bool is_even(int n) const {
return (n & 1) == 0;
}
inline int clamp(int value, int min, int max) const {
if (value < min) return min;
if (value > max) return max;
return value;
}
inline float lerp(float a, float b, float t) const {
return a + t * (b - a);
}
// 不适合inline的大函数
void complex_calculation(std::vector<int>& data) {
// 复杂操作,不应该inline
std::sort(data.begin(), data.end());
// ... 更多复杂操作
}
// 递归函数通常不能inline
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
};
// 7. inline与constexpr的结合(C++11起)
class CompileTimeMath {
public:
// constexpr函数默认inline
constexpr static int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
constexpr static bool is_prime(int n) {
if (n <= 1) return false;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) return false;
}
return true;
}
// C++14起允许更复杂的constexpr函数
constexpr static int sum_squares(int n) {
int sum = 0;
for (int i = 1; i <= n; ++i) {
sum += i * i;
}
return sum;
}
};
void constexpr_inline_demo() {
std::cout << "\n=== constexpr inline函数 ===" << std::endl;
// 编译期计算
constexpr int fact_5 = CompileTimeMath::factorial(5);
constexpr bool prime_17 = CompileTimeMath::is_prime(17);
constexpr int squares_10 = CompileTimeMath::sum_squares(10);
std::cout << "5! = " << fact_5 << std::endl;
std::cout << "17是质数? " << (prime_17 ? "是" : "否") << std::endl;
std::cout << "1²+2²+...+10² = " << squares_10 << std::endl;
// 运行时使用
for (int i = 0; i <= 10; ++i) {
std::cout << i << "! = " << CompileTimeMath::factorial(i) << std::endl;
}
}
// 8. 实际工程中的inline使用
class StringUtils {
public:
// 适合inline的字符串辅助函数
inline static bool starts_with(const std::string& str, const std::string& prefix) {
return str.size() >= prefix.size() &&
str.compare(0, prefix.size(), prefix) == 0;
}
inline static bool ends_with(const std::string& str, const std::string& suffix) {
return str.size() >= suffix.size() &&
str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}
inline static std::string trim_left(const std::string& str) {
size_t start = str.find_first_not_of(" \t\n\r\f\v");
return (start == std::string::npos) ? "" : str.substr(start);
}
inline static std::string trim_right(const std::string& str) {
size_t end = str.find_last_not_of(" \t\n\r\f\v");
return (end == std::string::npos) ? "" : str.substr(0, end + 1);
}
inline static std::string trim(const std::string& str) {
return trim_right(trim_left(str));
}
// 不适合inline的复杂字符串处理
static std::vector<std::string> split(const std::string& str, char delimiter);
};
int main() {
macro_problems_demo();
complex_macro_issues();
inline_function_demo();
constexpr_inline_demo();
std::cout << "\n=== 字符串工具示例 ===" << std::endl;
std::string text = " Hello, World! ";
std::cout << "原始: \"" << text << "\"" << std::endl;
std::cout << "修剪后: \"" << StringUtils::trim(text) << "\"" << std::endl;
std::string filename = "document.pdf";
std::cout << "文件名: " << filename << std::endl;
std::cout << "是否以.pdf结尾? "
<< (StringUtils::ends_with(filename, ".pdf") ? "是" : "否") << std::endl;
std::string url = "https://example.com";
std::cout << "URL: " << url << std::endl;
std::cout << "是否以https开头? "
<< (StringUtils::starts_with(url, "https") ? "是" : "否") << std::endl;
return 0;
}
6.3 inline的注意事项和最佳实践
cpp
#include <iostream>
#include <vector>
#include <algorithm>
// 1. inline只是建议,编译器可能忽略
class InlineGuidelines {
public:
// 适合inline的情况:
// a) 简单的getter/setter
inline int get_value() const { return value_; }
inline void set_value(int v) { value_ = v; }
// b) 简单的数学运算
inline static double degrees_to_radians(double degrees) {
return degrees * 3.141592653589793 / 180.0;
}
// c) 小的工具函数
inline static bool is_power_of_two(unsigned int n) {
return n && !(n & (n - 1));
}
// 不适合inline的情况:
// a) 函数体太大
void large_function() {
// 几十行或上百行代码
// 不应该标记为inline
}
// b) 递归函数
int recursive_function(int n) {
if (n <= 1) return 1;
return n * recursive_function(n - 1);
}
// c) 包含循环的函数
void process_array(int* arr, int size) {
for (int i = 0; i < size; ++i) {
// 复杂处理
}
}
// d) 虚函数
virtual void virtual_function() {
// 虚函数通常不能inline,因为需要动态绑定
}
private:
int value_ = 0;
};
// 2. inline函数的可见性问题
// 头文件中:
namespace Math {
inline int add(int a, int b) {
return a + b;
}
inline int multiply(int a, int b) {
return a * b;
}
}
// 多个源文件包含这个头文件时,不会产生重复定义错误
// 因为inline函数具有外部链接性
// 3. inline变量的使用(C++17)
class AppConfig {
public:
// inline静态成员变量(C++17)
inline static const std::string app_name = "MyApplication";
inline static const int max_connections = 100;
inline static const double pi = 3.141592653589793;
// inline静态成员函数
inline static const std::string& get_version() {
static const std::string version = "1.0.0";
return version;
}
};
// 4. 性能测试:inline vs 非inline
#include <chrono>
class PerformanceTest {
public:
// 标记为inline
inline int inline_multiply(int a, int b) {
return a * b;
}
// 不标记为inline(但编译器可能仍会inline)
int noninline_multiply(int a, int b) {
return a * b;
}
void run_test() {
const int iterations = 100000000;
volatile int result = 0; // 防止优化
// 测试inline版本
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i) {
result = inline_multiply(i, i + 1);
}
auto end = std::chrono::high_resolution_clock::now();
auto inline_duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
// 测试非inline版本
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i) {
result = noninline_multiply(i, i + 1);
}
end = std::chrono::high_resolution_clock::now();
auto noninline_duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "inline版本: " << inline_duration.count() << "ms" << std::endl;
std::cout << "非inline版本: " << noninline_duration.count() << "ms" << std::endl;
std::cout << "性能差异: "
<< (noninline_duration.count() * 100.0 / inline_duration.count() - 100)
<< "%" << std::endl;
// 注意:现代编译器很智能,可能都会inline
// 实际差异可能很小
}
};
// 5. inline与模板
template<typename Container>
class ContainerUtils {
public:
// 模板函数通常定义在头文件中,隐式inline
static typename Container::value_type
sum(const Container& container) {
typename Container::value_type total = 0;
for (const auto& item : container) {
total += item;
}
return total;
}
template<typename Predicate>
static bool all_of(const Container& container, Predicate pred) {
for (const auto& item : container) {
if (!pred(item)) return false;
}
return true;
}
};
// 6. 实际项目中的inline策略
class ProjectGuidelines {
public:
// 策略1:在类定义中实现的成员函数默认inline
class Point {
public:
Point(int x, int y) : x_(x), y_(y) {}
int x() const { return x_; } // 隐式inline
int y() const { return y_; } // 隐式inline
void set_x(int x) { x_ = x; } // 隐式inline
void set_y(int y) { y_ = y; } // 隐式inline
private:
int x_, y_;
};
// 策略2:短小的自由函数标记为inline
inline int clamp(int value, int min, int max) {
return (value < min) ? min : (value > max) ? max : value;
}
inline float lerp(float a, float b, float t) {
return a + t * (b - a);
}
// 策略3:模板函数放在头文件中
template<typename T>
T min(T a, T b) {
return a < b ? a : b;
}
// 策略4:复杂的函数实现放在源文件中
void complex_algorithm(std::vector<int>& data);
};
int main() {
std::cout << "=== inline最佳实践示例 ===" << std::endl;
// 使用inline变量(C++17)
std::cout << "应用名称: " << AppConfig::app_name << std::endl;
std::cout << "最大连接数: " << AppConfig::max_connections << std::endl;
std::cout << "版本: " << AppConfig::get_version() << std::endl;
std::cout << "PI: " << AppConfig::pi << std::endl;
// 使用数学函数
std::cout << "45度 = " << InlineGuidelines::degrees_to_radians(45) << " 弧度" << std::endl;
// 检查2的幂
std::cout << "8是2的幂? " << (InlineGuidelines::is_power_of_two(8) ? "是" : "否") << std::endl;
std::cout << "10是2的幂? " << (InlineGuidelines::is_power_of_two(10) ? "是" : "否") << std::endl;
// 容器工具
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::cout << "总和: " << ContainerUtils<std::vector<int>>::sum(numbers) << std::endl;
bool all_positive = ContainerUtils<std::vector<int>>::all_of(
numbers, [](int x) { return x > 0; });
std::cout << "所有数都大于0? " << (all_positive ? "是" : "否") << std::endl;
// 项目指南示例
ProjectGuidelines::Point p(10, 20);
std::cout << "点坐标: (" << p.x() << ", " << p.y() << ")" << std::endl;
std::cout << "clamp(15, 0, 10) = " << ProjectGuidelines::clamp(15, 0, 10) << std::endl;
std::cout << "lerp(0, 10, 0.5) = " << ProjectGuidelines::lerp(0, 10, 0.5) << std::endl;
std::cout << "min(5, 3) = " << ProjectGuidelines::min(5, 3) << std::endl;
// 性能测试
std::cout << "\n=== 性能测试 ===" << std::endl;
PerformanceTest test;
test.run_test();
return 0;
}
七、更安全的空指针:nullptr
7.1 NULL的问题和nullptr的解决方案
cpp
#include <iostream>
#include <cstddef> // 定义NULL
// C语言中的NULL问题
void null_problems_demo() {
std::cout << "=== NULL的问题 ===" << std::endl;
// NULL在C++中通常定义为0或0L
#ifdef __cplusplus
std::cout << "C++中NULL的定义: "
<< (NULL == 0 ? "0" : "0L") << std::endl;
#endif
// 问题1:类型不明确
void func(int x) {
std::cout << "调用func(int): " << x << std::endl;
}
void func(char* ptr) {
std::cout << "调用func(char*): " << (ptr ? "非空" : "空") << std::endl;
}
// 在C++中,NULL是整数0,而不是指针
func(NULL); // 调用func(int),而不是func(char*)
// 问题2:模板推导问题
template<typename T>
void process(T* ptr) {
std::cout << "处理指针" << std::endl;
}
template<typename T>
void process(int value) {
std::cout << "处理整数" << std::endl;
}
process<char>(NULL); // 应该调用指针版本,但NULL是整数
std::cout << std::endl;
}
// nullptr的解决方案
void nullptr_solution_demo() {
std::cout << "=== nullptr的解决方案 ===" << std::endl;
// nullptr是C++11引入的空指针字面量
void func(int x) {
std::cout << "调用func(int): " << x << std::endl;
}
void func(char* ptr) {
std::cout << "调用func(char*): " << (ptr ? "非空" : "空") << std::endl;
}
void func(std::nullptr_t) {
std::cout << "调用func(nullptr_t)" << std::endl;
}
// 明确调用指针版本
func(nullptr); // 调用func(char*)或func(nullptr_t)
func(0); // 调用func(int)
// nullptr的类型是std::nullptr_t
std::nullptr_t null_value = nullptr;
func(null_value); // 调用func(nullptr_t)
std::cout << std::endl;
}
// 模板编程中的nullptr
template<typename T>
class SmartPointer {
private:
T* ptr_;
public:
// 构造函数
explicit SmartPointer(T* ptr = nullptr) : ptr_(ptr) {
std::cout << "创建SmartPointer" << std::endl;
}
// 析构函数
~SmartPointer() {
delete ptr_;
std::cout << "销毁SmartPointer" << std::endl;
}
// 检查是否为空
bool is_null() const {
return ptr_ == nullptr;
}
// 重载bool转换
explicit operator bool() const {
return ptr_ != nullptr;
}
// 重载解引用运算符
T& operator*() const {
if (!ptr_) {
throw std::runtime_error("解引用空指针");
}
return *ptr_;
}
T* operator->() const {
if (!ptr_) {
throw std::runtime_error("访问空指针成员");
}
return ptr_;
}
// 删除拷贝操作
SmartPointer(const SmartPointer&) = delete;
SmartPointer& operator=(const SmartPointer&) = delete;
// 允许移动
SmartPointer(SmartPointer&& other) noexcept : ptr_(other.ptr_) {
other.ptr_ = nullptr;
}
SmartPointer& operator=(SmartPointer&& other) noexcept {
if (this != &other) {
delete ptr_;
ptr_ = other.ptr_;
other.ptr_ = nullptr;
}
return *this;
}
};
// nullptr在不同场景的应用
class Database {
public:
class Connection {
public:
virtual void connect() = 0;
virtual void disconnect() = 0;
virtual ~Connection() = default;
};
class Statement {
public:
virtual void execute(const std::string& query) = 0;
virtual ~Statement() = default;
};
// 工厂方法,返回指针可能为空
virtual Connection* create_connection() = 0;
virtual Statement* prepare_statement(const std::string& query) = 0;
virtual ~Database() = default;
};
// 现代C++中的空指针用法
void modern_nullptr_usage() {
std::cout << "=== 现代C++中的nullptr ===" << std::endl;
// 1. 初始化指针
int* ptr1 = nullptr; // 推荐
char* ptr2 = nullptr; // 推荐
double* ptr3 = nullptr; // 推荐
// 2. 与智能指针一起使用
std::unique_ptr<int> smart_ptr1 = nullptr;
std::shared_ptr<double> smart_ptr2 = nullptr;
std::weak_ptr<char> smart_ptr3 = nullptr;
// 3. 在条件语句中
int* data_ptr = nullptr;
if (data_ptr == nullptr) {
std::cout << "指针为空" << std::endl;
}
if (!data_ptr) { // 等价写法
std::cout << "指针为空(简写)" << std::endl;
}
// 4. 作为函数返回值
auto find_value = [](const std::vector<int>& vec, int target) -> int* {
for (auto& item : vec) {
if (item == target) {
return &item;
}
}
return nullptr; // 没找到
};
std::vector<int> numbers = {1, 2, 3, 4, 5};
int* found = find_value(numbers, 3);
if (found != nullptr) {
std::cout << "找到值: " << *found << std::endl;
}
// 5. 与auto一起使用
auto ptr4 = nullptr; // ptr4的类型是std::nullptr_t
// auto ptr5: int* = nullptr; // 明确类型
std::cout << std::endl;
}
// nullptr的类型安全特性
void type_safety_demo() {
std::cout << "=== nullptr的类型安全 ===" << std::endl;
// nullptr不能转换为整数
// int x = nullptr; // 错误:不能将nullptr转换为int
// 但可以显式转换为布尔值
bool b = (nullptr == nullptr); // true
std::cout << "nullptr == nullptr: " << std::boolalpha << b << std::endl;
// nullptr可以转换为任何指针类型
int* int_ptr = nullptr;
char* char_ptr = nullptr;
void* void_ptr = nullptr;
// nullptr有自己的类型:std::nullptr_t
std::nullptr_t null_val = nullptr;
// 可以重载接受nullptr_t的函数
void process(int* ptr) {
std::cout << "处理int指针" << std::endl;
}
void process(std::nullptr_t) {
std::cout << "处理nullptr" << std::endl;
}
process(nullptr); // 调用process(nullptr_t)
process(int_ptr); // 调用process(int*)
std::cout << std::endl;
}
// 实际工程中的nullptr应用
class ResourceManager {
private:
class Resource {
public:
Resource() { std::cout << "创建资源" << std::endl; }
~Resource() { std::cout << "销毁资源" << std::endl; }
void use() { std::cout << "使用资源" << std::endl; }
};
Resource* resource_ = nullptr;
public:
ResourceManager() = default;
~ResourceManager() {
release();
}
// 获取资源(延迟初始化)
Resource* get_resource() {
if (resource_ == nullptr) {
resource_ = new Resource();
}
return resource_;
}
// 释放资源
void release() {
if (resource_ != nullptr) {
delete resource_;
resource_ = nullptr; // 重要:设置为nullptr避免悬空指针
}
}
// 检查是否有资源
bool has_resource() const {
return resource_ != nullptr;
}
// 重置资源
void reset(Resource* new_resource = nullptr) {
release();
resource_ = new_resource;
}
// 移动语义
ResourceManager(ResourceManager&& other) noexcept
: resource_(other.resource_) {
other.resource_ = nullptr;
}
ResourceManager& operator=(ResourceManager&& other) noexcept {
if (this != &other) {
release();
resource_ = other.resource_;
other.resource_ = nullptr;
}
return *this;
}
// 删除拷贝
ResourceManager(const ResourceManager&) = delete;
ResourceManager& operator=(const ResourceManager&) = delete;
};
// nullptr在模板元编程中的应用
template<typename T>
struct TypeInfo {
static const char* name() {
return "unknown";
}
};
template<>
struct TypeInfo<int> {
static const char* name() {
return "int";
}
};
template<>
struct TypeInfo<std::nullptr_t> {
static const char* name() {
return "nullptr_t";
}
};
template<typename T>
void print_type() {
std::cout << "类型: " << TypeInfo<T>::name() << std::endl;
}
int main() {
null_problems_demo();
nullptr_solution_demo();
modern_nullptr_usage();
type_safety_demo();
std::cout << "=== 资源管理器示例 ===" << std::endl;
{
ResourceManager manager;
std::cout << "有资源吗? " << (manager.has_resource() ? "是" : "否") << std::endl;
auto* res = manager.get_resource();
res->use();
std::cout << "有资源吗? " << (manager.has_resource() ? "是" : "否") << std::endl;
manager.release();
std::cout << "有资源吗? " << (manager.has_resource() ? "是" : "否") << std::endl;
// 使用reset
manager.reset(new ResourceManager::Resource());
// 移动语义
ResourceManager manager2 = std::move(manager);
std::cout << "移动后,manager有资源吗? "
<< (manager.has_resource() ? "是" : "否") << std::endl;
} // 自动释放资源
std::cout << "\n=== 模板元编程中的nullptr ===" << std::endl;
print_type<int>();
print_type<std::nullptr_t>();
// 使用SmartPointer
std::cout << "\n=== 智能指针示例 ===" << std::endl;
{
SmartPointer<int> ptr(new int(42));
if (ptr) { // 使用bool转换
std::cout << "值: " << *ptr << std::endl;
}
SmartPointer<int> empty_ptr(nullptr);
std::cout << "空指针? " << (empty_ptr.is_null() ? "是" : "否") << std::endl;
// 移动语义
SmartPointer<int> moved_ptr = std::move(ptr);
std::cout << "移动后,原指针空? " << (ptr.is_null() ? "是" : "否") << std::endl;
}
return 0;
}
7.2 从NULL迁移到nullptr
cpp
#include <iostream>
#include <memory>
#include <cstdlib> // 包含NULL的定义
// 旧代码库示例(使用NULL)
class LegacyCode {
public:
// 旧的函数声明
void* allocate_memory_old(size_t size) {
void* ptr = std::malloc(size);
if (ptr == NULL) { // 使用NULL
throw std::bad_alloc();
}
return ptr;
}
// 旧的重载函数
void process_old(int value) {
std::cout << "处理整数: " << value << std::endl;
}
void process_old(void* ptr) {
std::cout << "处理指针: " << (ptr ? "非空" : "空") << std::endl;
}
};
// 新代码库(使用nullptr)
class ModernCode {
public:
// 使用nullptr的现代代码
void* allocate_memory_new(size_t size) {
void* ptr = std::malloc(size);
if (ptr == nullptr) { // 使用nullptr
throw std::bad_alloc();
}
return ptr;
}
// 现代重载函数
void process_new(int value) {
std::cout << "处理整数: " << value << std::endl;
}
void process_new(void* ptr) {
std::cout << "处理指针: " << (ptr ? "非空" : "空") << std::endl;
}
// 专门处理nullptr的重载
void process_new(std::nullptr_t) {
std::cout << "处理nullptr" << std::endl;
}
};
// 迁移工具和技巧
class MigrationTools {
public:
// 技巧1:使用宏进行渐进式迁移
#ifdef USE_NULLPTR
#define MY_NULL nullptr
#else
#define MY_NULL NULL
#endif
// 技巧2:类型安全的空指针包装
template<typename T>
class NullPointer {
public:
operator T*() const { return nullptr; }
// 与nullptr比较
bool operator==(std::nullptr_t) const { return true; }
bool operator!=(std::nullptr_t) const { return false; }
// 与指针比较
template<typename U>
bool operator==(U* ptr) const { return ptr == nullptr; }
template<typename U>
bool operator!=(U* ptr) const { return ptr != nullptr; }
};
// 使用示例
template<typename T>
static NullPointer<T> null() {
return NullPointer<T>();
}
// 技巧3:检查代码中NULL的使用
static void check_for_null_usage() {
// 在大型项目中,可以使用静态分析工具
// 或编写脚本检查NULL的使用
std::cout << "检查NULL使用..." << std::endl;
}
// 技巧4:为旧代码提供适配器
class LegacyAdapter {
public:
// 将nullptr转换为旧代码期望的形式
static const void* to_legacy(std::nullptr_t) {
return static_cast<const void*>(0);
}
// 将旧代码的NULL转换为nullptr
static std::nullptr_t from_legacy(const void* ptr) {
return ptr ? nullptr : nullptr;
}
};
};
// 实际迁移案例
class DatabaseConnection {
private:
void* native_handle_ = nullptr; // 使用nullptr初始化
public:
DatabaseConnection() = default;
explicit DatabaseConnection(void* handle)
: native_handle_(handle) {}
// 旧的接口(兼容性)
bool connect_old(const char* host, int port, void* options = NULL) {
// 模拟连接
if (options == NULL) {
std::cout << "使用默认选项连接" << std::endl;
} else {
std::cout << "使用自定义选项连接" << std::endl;
}
return true;
}
// 新的接口(推荐)
bool connect_new(const char* host, int port, void* options = nullptr) {
// 模拟连接
if (options == nullptr) {
std::cout << "使用默认选项连接" << std::endl;
} else {
std::cout << "使用自定义选项连接" << std::endl;
}
return true;
}
// 检查连接是否有效
bool is_connected() const {
return native_handle_ != nullptr;
}
// 断开连接
void disconnect() {
if (native_handle_ != nullptr) {
// 释放资源
native_handle_ = nullptr; // 设置为nullptr
}
}
// 移动语义
DatabaseConnection(DatabaseConnection&& other) noexcept
: native_handle_(other.native_handle_) {
other.native_handle_ = nullptr;
}
DatabaseConnection& operator=(DatabaseConnection&& other) noexcept {
if (this != &other) {
disconnect();
native_handle_ = other.native_handle_;
other.native_handle_ = nullptr;
}
return *this;
}
~DatabaseConnection() {
disconnect();
}
// 删除拷贝
DatabaseConnection(const DatabaseConnection&) = delete;
DatabaseConnection& operator=(const DatabaseConnection&) = delete;
};
// nullptr在现代C++特性中的应用
class ModernFeatures {
public:
// 1. 与constexpr结合
constexpr static std::nullptr_t get_null() {
return nullptr;
}
// 2. 在静态断言中
static_assert(sizeof(nullptr) == sizeof(void*),
"nullptr大小应与指针相同");
// 3. 在noexcept规范中
void safe_function() noexcept(nullptr) {
// C++17:条件性noexcept
}
// 4. 在模板元编程中
template<typename T>
struct IsPointer {
static constexpr bool value = false;
};
template<typename T>
struct IsPointer<T*> {
static constexpr bool value = true;
};
// 5. 与decltype一起使用
auto get_pointer_type() -> decltype(nullptr) {
return nullptr;
}
// 6. 在lambda表达式中
auto get_null_lambda = []() -> auto* {
return nullptr;
};
// 7. 在可变参数模板中
template<typename... Args>
void process_args(Args... args) {
// 处理参数,包括nullptr
}
};
// 最佳实践总结
class BestPractices {
public:
// 规则1:始终使用nullptr,而不是NULL或0
void rule1() {
int* ptr1 = nullptr; // 正确
// int* ptr2 = NULL; // 避免
// int* ptr3 = 0; // 避免
}
// 规则2:使用nullptr初始化所有指针
class Example {
int* data_ = nullptr; // 正确:成员初始化
std::unique_ptr<int> ptr_ = nullptr; // 正确:智能指针
public:
Example() = default;
explicit Example(int* data)
: data_(data) {} // 允许非nullptr初始化
};
// 规则3:在比较中使用nullptr
void rule3(int* ptr) {
if (ptr == nullptr) { // 显式比较
// 处理空指针
}
if (!ptr) { // 隐式比较(等效)
// 处理空指针
}
}
// 规则4:使用nullptr作为默认参数
void rule4(int* ptr = nullptr) { // 正确
if (ptr) {
// 使用指针
}
}
// 规则5:释放后立即设置为nullptr
void rule5() {
int* ptr = new int(42);
// 使用ptr...
delete ptr;
ptr = nullptr; // 重要:避免悬空指针
}
// 规则6:在移动操作后设置为nullptr
class Movable {
int* resource_ = nullptr;
public:
Movable(int* res) : resource_(res) {}
Movable(Movable&& other) noexcept
: resource_(other.resource_) {
other.resource_ = nullptr; // 重要:移动后置空
}
~Movable() {
delete resource_;
}
};
// 规则7:在接口中使用nullptr_t表示空指针参数
void rule7(std::nullptr_t) {
std::cout << "专门处理nullptr" << std::endl;
}
};
int main() {
std::cout << "=== 从NULL迁移到nullptr ===" << std::endl;
// 旧代码使用
LegacyCode legacy;
legacy.process_old(NULL); // 调用process_old(int)
legacy.process_old((void*)NULL); // 调用process_old(void*)
// 新代码使用
ModernCode modern;
modern.process_new(0); // 调用process_new(int)
modern.process_new(nullptr); // 调用process_new(nullptr_t)或process_new(void*)
// 迁移工具使用
MigrationTools::NullPointer<int> null_int = MigrationTools::null<int>();
int* test_ptr = null_int; // 转换为nullptr
if (test_ptr == nullptr) {
std::cout << "NullPointer转换为nullptr" << std::endl;
}
// 数据库连接示例
std::cout << "\n=== 数据库连接示例 ===" << std::endl;
DatabaseConnection conn1;
DatabaseConnection conn2;
// 旧方式
conn1.connect_old("localhost", 3306, NULL);
// 新方式
conn2.connect_new("localhost", 3306, nullptr);
std::cout << "conn1已连接? " << (conn1.is_connected() ? "是" : "否") << std::endl;
std::cout << "conn2已连接? " << (conn2.is_connected() ? "是" : "否") << std::endl;
// 移动语义
DatabaseConnection conn3 = std::move(conn2);
std::cout << "移动后conn2已连接? " << (conn2.is_connected() ? "是" : "否") << std::endl;
std::cout << "移动后conn3已连接? " << (conn3.is_connected() ? "是" : "否") << std::endl;
// 现代特性
std::cout << "\n=== 现代特性 ===" << std::endl;
ModernFeatures features;
constexpr auto null = ModernFeatures::get_null();
std::cout << "编译期nullptr: " << (null == nullptr ? "是nullptr" : "不是nullptr") << std::endl;
// 类型检查
std::cout << "int*是指针类型? "
<< (ModernFeatures::IsPointer<int*>::value ? "是" : "否") << std::endl;
std::cout << "int是指针类型? "
<< (ModernFeatures::IsPointer<int>::value ? "是" : "否") << std::endl;
// 最佳实践
std::cout << "\n=== 最佳实践 ===" << std::endl;
BestPractices practices;
int value = 42;
practices.rule3(&value); // 非空指针
practices.rule3(nullptr); // 空指针
practices.rule4(); // 使用默认参数nullptr
practices.rule4(&value); // 传递非空指针
practices.rule7(nullptr); // 专门处理nullptr
return 0;
}
八、简单了解类:class(基础概念)
8.1 从C结构体到C++类
cpp
#include <iostream>
#include <cstring>
#include <string>
// C语言的结构体
struct CStudent {
char name[50];
int age;
float score;
};
// C语言操作结构体的函数
void CStudent_init(struct CStudent* stu, const char* name, int age, float score) {
strncpy(stu->name, name, sizeof(stu->name) - 1);
stu->name[sizeof(stu->name) - 1] = '\0';
stu->age = age;
stu->score = score;
}
void CStudent_print(const struct CStudent* stu) {
printf("姓名: %s, 年龄: %d, 成绩: %.1f\n", stu->name, stu->age, stu->score);
}
// C++的类
class Student {
private:
// 私有成员变量:外部不能直接访问
std::string name_; // 使用std::string而不是char数组
int age_;
float score_;
public:
// 构造函数:对象创建时自动调用
Student(const std::string& name, int age, float score)
: name_(name), age_(age), score_(score) {
std::cout << "创建Student: " << name_ << std::endl;
}
// 默认构造函数
Student() : name_("未知"), age_(0), score_(0.0f) {
std::cout << "创建默认Student" << std::endl;
}
// 拷贝构造函数
Student(const Student& other)
: name_(other.name_), age_(other.age_), score_(other.score_) {
std::cout << "拷贝Student: " << name_ << std::endl;
}
// 析构函数:对象销毁时自动调用
~Student() {
std::cout << "销毁Student: " << name_ << std::endl;
}
// 公有成员函数:外部可以调用
void print() const {
std::cout << "姓名: " << name_
<< ", 年龄: " << age_
<< ", 成绩: " << score_ << std::endl;
}
// Getter方法:访问私有成员
std::string get_name() const {
return name_;
}
int get_age() const {
return age_;
}
float get_score() const {
return score_;
}
// Setter方法:修改私有成员(可以添加验证)
void set_name(const std::string& name) {
if (!name.empty()) {
name_ = name;
}
}
void set_age(int age) {
if (age >= 0 && age <= 150) {
age_ = age;
}
}
void set_score(float score) {
if (score >= 0.0f && score <= 100.0f) {
score_ = score;
}
}
// 成员函数可以访问私有成员
bool is_excellent() const {
return score_ >= 90.0f;
}
void promote() {
age_++;
score_ += 5.0f; // 假设每年进步
if (score_ > 100.0f) score_ = 100.0f;
}
};
// 对比C和C++的方式
void compare_c_cpp() {
std::cout << "=== C语言方式 ===" << std::endl;
struct CStudent c_stu;
CStudent_init(&c_stu, "张三", 20, 85.5);
CStudent_print(&c_stu);
// C语言可以直接修改结构体成员(不安全)
c_stu.score = 120.0f; // 无效的成绩,但编译器不会阻止
std::cout << "\n=== C++方式 ===" << std::endl;
Student stu("李四", 21, 88.5);
stu.print();
// 不能直接修改私有成员
// stu.score_ = 120.0f; // 编译错误:score_是私有的
// 必须通过公有接口
stu.set_score(92.5f);
stu.print();
std::cout << "是否优秀: " << (stu.is_excellent() ? "是" : "否") << std::endl;
stu.promote();
std::cout << "升级后: ";
stu.print();
}
// 类的更多特性
class Rectangle {
private:
double width_;
double height_;
public:
// 构造函数重载
Rectangle() : width_(1.0), height_(1.0) {}
Rectangle(double side) : width_(side), height_(side) {}
Rectangle(double width, double height)
: width_(width), height_(height) {}
// 成员函数
double area() const {
return width_ * height_;
}
double perimeter() const {
return 2 * (width_ + height_);
}
// 重载运算符
Rectangle operator+(const Rectangle& other) const {
return Rectangle(width_ + other.width_,
height_ + other.height_);
}
Rectangle operator*(double scale) const {
return Rectangle(width_ * scale, height_ * scale);
}
// 友元函数:可以访问私有成员的非成员函数
friend Rectangle operator*(double scale, const Rectangle& rect);
// 静态成员:属于类本身,而不是对象
static int get_count() {
return count_;
}
private:
static int count_; // 静态成员变量声明
};
// 静态成员定义(必须在类外定义)
int Rectangle::count_ = 0;
// 友元函数定义
Rectangle operator*(double scale, const Rectangle& rect) {
return Rectangle(rect.width_ * scale, rect.height_ * scale);
}
// const成员函数示例
class BankAccount {
private:
std::string owner_;
mutable double balance_; // mutable允许在const函数中修改
public:
BankAccount(const std::string& owner, double balance)
: owner_(owner), balance_(balance) {}
// const成员函数:承诺不修改对象状态
const std::string& get_owner() const {
return owner_;
}
double get_balance() const {
return balance_;
}
// 非const成员函数:可以修改对象状态
void deposit(double amount) {
if (amount > 0) {
balance_ += amount;
}
}
bool withdraw(double amount) {
if (amount > 0 && balance_ >= amount) {
balance_ -= amount;
return true;
}
return false;
}
// const函数中修改mutable成员
void add_interest(double rate) const {
// 虽然函数是const,但可以修改mutable成员
balance_ *= (1.0 + rate);
}
};
int main() {
compare_c_cpp();
std::cout << "\n=== 矩形类示例 ===" << std::endl;
Rectangle rect1; // 使用默认构造函数
Rectangle rect2(5.0); // 使用单参数构造函数
Rectangle rect3(3.0, 4.0); // 使用双参数构造函数
std::cout << "rect1面积: " << rect1.area() << std::endl;
std::cout << "rect2面积: " << rect2.area() << std::endl;
std::cout << "rect3面积: " << rect3.area() << std::endl;
std::cout << "rect3周长: " << rect3.perimeter() << std::endl;
// 运算符重载
Rectangle rect4 = rect2 + rect3;
std::cout << "rect2 + rect3面积: " << rect4.area() << std::endl;
Rectangle rect5 = rect3 * 2.0;
std::cout << "rect3 * 2面积: " << rect5.area() << std::endl;
Rectangle rect6 = 3.0 * rect3;
std::cout << "3 * rect3面积: " << rect6.area() << std::endl;
// 静态成员
std::cout << "矩形计数: " << Rectangle::get_count() << std::endl;
std::cout << "\n=== 银行账户示例 ===" << std::endl;
BankAccount account("张三", 1000.0);
std::cout << "账户所有人: " << account.get_owner() << std::endl;
std::cout << "账户余额: " << account.get_balance() << std::endl;
account.deposit(500.0);
std::cout << "存款后余额: " << account.get_balance() << std::endl;
if (account.withdraw(300.0)) {
std::cout << "取款成功,余额: " << account.get_balance() << std::endl;
}
// const对象只能调用const成员函数
const BankAccount& const_account = account;
std::cout << "const引用获取余额: " << const_account.get_balance() << std::endl;
// mutable成员可以在const函数中修改
const_account.add_interest(0.05);
std::cout << "添加利息后余额: " << account.get_balance() << std::endl;
return 0;
}
8.2 类的封装、继承和多态基础
cpp
#include <iostream>
#include <string>
#include <vector>
#include <memory>
// 1. 封装:隐藏实现细节,提供公有接口
class Employee {
private:
std::string name_;
int id_;
double salary_;
protected:
// 保护成员:派生类可以访问,外部不能访问
std::string department_;
public:
Employee(const std::string& name, int id, double salary,
const std::string& department)
: name_(name), id_(id), salary_(salary), department_(department) {}
virtual ~Employee() = default;
// 公有接口
virtual void work() const {
std::cout << name_ << "正在工作..." << std::endl;
}
virtual void print_info() const {
std::cout << "员工信息:" << std::endl;
std::cout << " 姓名: " << name_ << std::endl;
std::cout << " 工号: " << id_ << std::endl;
std::cout << " 部门: " << department_ << std::endl;
std::cout << " 薪水: " << salary_ << std::endl;
}
// Getter和Setter
std::string get_name() const { return name_; }
int get_id() const { return id_; }
double get_salary() const { return salary_; }
void set_salary(double salary) {
if (salary > 0) {
salary_ = salary;
}
}
// 静态成员
static int get_total_employees() {
return total_employees_;
}
private:
static int total_employees_;
};
int Employee::total_employees_ = 0;
// 2. 继承:创建新类,复用现有类的特性
class Manager : public Employee {
private:
int team_size_;
std::vector<Employee*> team_;
public:
Manager(const std::string& name, int id, double salary,
const std::string& department, int team_size)
: Employee(name, id, salary, department), team_size_(team_size) {}
// 重写基类虚函数(多态)
void work() const override {
std::cout << get_name() << "正在管理团队..." << std::endl;
}
void print_info() const override {
Employee::print_info(); // 调用基类版本
std::cout << " 团队人数: " << team_size_ << std::endl;
std::cout << " 角色: 经理" << std::endl;
}
// 特有的功能
void add_to_team(Employee* employee) {
if (team_.size() < static_cast<size_t>(team_size_)) {
team_.push_back(employee);
std::cout << employee->get_name() << "已加入团队" << std::endl;
}
}
void hold_meeting() const {
std::cout << get_name() << "正在召开团队会议" << std::endl;
}
// 访问保护成员
void change_department(const std::string& new_dept) {
department_ = new_dept;
std::cout << get_name() << "的部门已改为: " << department_ << std::endl;
}
};
// 3. 多态:通过基类指针/引用调用派生类函数
class Developer : public Employee {
private:
std::string programming_language_;
public:
Developer(const std::string& name, int id, double salary,
const std::string& department, const std::string& language)
: Employee(name, id, salary, department),
programming_language_(language) {}
void work() const override {
std::cout << get_name() << "正在用" << programming_language_
<< "编程..." << std::endl;
}
void print_info() const override {
Employee::print_info();
std::cout << " 编程语言: " << programming_language_ << std::endl;
std::cout << " 角色: 开发人员" << std::endl;
}
void debug_code() const {
std::cout << get_name() << "正在调试代码..." << std::endl;
}
};
// 4. 抽象类:包含纯虚函数的类
class Shape {
public:
virtual ~Shape() = default;
// 纯虚函数:必须在派生类中实现
virtual double area() const = 0;
virtual double perimeter() const = 0;
virtual void draw() const = 0;
// 普通虚函数:可以有默认实现
virtual void scale(double factor) {
std::cout << "缩放图形,因子: " << factor << std::endl;
}
// 非虚函数:不能在派生类中重写
void print_info() const {
std::cout << "面积: " << area()
<< ", 周长: " << perimeter() << std::endl;
}
};
// 具体派生类
class Circle : public Shape {
private:
double radius_;
public:
explicit Circle(double radius) : radius_(radius) {}
double area() const override {
return 3.141592653589793 * radius_ * radius_;
}
double perimeter() const override {
return 2 * 3.141592653589793 * radius_;
}
void draw() const override {
std::cout << "绘制圆形,半径: " << radius_ << std::endl;
}
void scale(double factor) override {
radius_ *= factor;
Shape::scale(factor); // 调用基类实现
}
};
class Rectangle : public Shape {
private:
double width_, height_;
public:
Rectangle(double width, double height)
: width_(width), height_(height) {}
double area() const override {
return width_ * height_;
}
double perimeter() const override {
return 2 * (width_ + height_);
}
void draw() const override {
std::cout << "绘制矩形,宽度: " << width_
<< ", 高度: " << height_ << std::endl;
}
void scale(double factor) override {
width_ *= factor;
height_ *= factor;
}
};
// 5. 组合:包含其他类对象作为成员
class Project {
private:
std::string name_;
Manager* manager_;
std::vector<Developer*> developers_;
std::vector<std::unique_ptr<Shape>> designs_;
public:
Project(const std::string& name, Manager* manager)
: name_(name), manager_(manager) {}
void add_developer(Developer* dev) {
developers_.push_back(dev);
std::cout << dev->get_name() << "已加入项目" << std::endl;
}
void add_design(std::unique_ptr<Shape> shape) {
designs_.push_back(std::move(shape));
}
void run() const {
std::cout << "\n=== 运行项目: " << name_ << " ===" << std::endl;
// 经理工作
if (manager_) {
manager_->work();
manager_->hold_meeting();
}
// 开发人员工作
std::cout << "\n开发团队工作:" << std::endl;
for (const auto& dev : developers_) {
dev->work();
dev->debug_code();
}
// 设计工作
std::cout << "\n设计工作:" << std::endl;
for (const auto& design : designs_) {
design->draw();
design->print_info();
}
std::cout << "项目完成!" << std::endl;
}
double total_design_area() const {
double total = 0.0;
for (const auto& design : designs_) {
total += design->area();
}
return total;
}
};
// 6. 工厂模式:创建对象的接口
class EmployeeFactory {
public:
static std::unique_ptr<Employee> create_employee(
const std::string& type,
const std::string& name,
int id,
double salary,
const std::string& department,
const std::string& extra = "") {
if (type == "manager") {
int team_size = extra.empty() ? 5 : std::stoi(extra);
return std::make_unique<Manager>(name, id, salary, department, team_size);
}
else if (type == "developer") {
std::string language = extra.empty() ? "C++" : extra;
return std::make_unique<Developer>(name, id, salary, department, language);
}
else if (type == "employee") {
return std::make_unique<Employee>(name, id, salary, department);
}
return nullptr;
}
};
int main() {
std::cout << "=== 面向对象编程示例 ===" << std::endl;
// 创建员工
Manager manager("王经理", 1001, 15000.0, "技术部", 8);
Developer dev1("张开发", 1002, 12000.0, "技术部", "C++");
Developer dev2("李开发", 1003, 11000.0, "技术部", "Python");
// 多态演示
std::cout << "\n=== 多态演示 ===" << std::endl;
std::vector<Employee*> employees = {&manager, &dev1, &dev2};
for (const auto& emp : employees) {
emp->work(); // 动态绑定,调用实际类型的work()
emp->print_info(); // 动态绑定
std::cout << std::endl;
}
// 使用基类指针
Employee* emp_ptr = &dev1;
emp_ptr->work(); // 调用Developer::work()
// 不能通过基类指针调用派生类特有函数
// emp_ptr->debug_code(); // 错误
// 需要向下转型(不安全)
if (auto dev_ptr = dynamic_cast<Developer*>(emp_ptr)) {
dev_ptr->debug_code();
}
// 抽象类和具体类
std::cout << "\n=== 抽象类演示 ===" << std::endl;
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(5.0));
shapes.push_back(std::make_unique<Rectangle>(4.0, 6.0));
for (const auto& shape : shapes) {
shape->draw();
shape->print_info();
// 缩放
shape->scale(1.5);
shape->print_info();
std::cout << std::endl;
}
// 项目组合示例
std::cout << "\n=== 项目组合演示 ===" << std::endl;
Project project("新一代AI系统", &manager);
project.add_developer(&dev1);
project.add_developer(&dev2);
project.add_design(std::make_unique<Circle>(10.0));
project.add_design(std::make_unique<Rectangle>(20.0, 30.0));
project.run();
std::cout << "总设计面积: " << project.total_design_area() << std::endl;
// 工厂模式示例
std::cout << "\n=== 工厂模式演示 ===" << std::endl;
auto emp1 = EmployeeFactory::create_employee(
"manager", "赵总", 2001, 20000.0, "管理部", "10");
auto emp2 = EmployeeFactory::create_employee(
"developer", "钱工", 2002, 13000.0, "研发部", "Java");
if (emp1) emp1->print_info();
if (emp2) emp2->print_info();
std::cout << "\n=== 封装演示 ===" << std::endl;
// 封装的好处:控制对数据的访问
Employee emp("孙员工", 3001, 8000.0, "市场部");
// 不能直接访问私有成员
// emp.salary_ = 1000000.0; // 错误
// 必须通过公有接口
emp.set_salary(9000.0); // 可以添加验证逻辑
std::cout << "新薪水: " << emp.get_salary() << std::endl;
// 尝试设置无效薪水
emp.set_salary(-1000.0); // 会被拒绝
std::cout << "无效设置后薪水: " << emp.get_salary() << std::endl;
return 0;
}
8.3 现代C++类特性
cpp
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <algorithm>
// 1. 默认和删除的特殊成员函数
class ModernClass {
private:
std::string name_;
std::unique_ptr<int[]> data_;
size_t size_;
public:
// 默认构造函数
ModernClass() = default;
// 自定义构造函数
explicit ModernClass(const std::string& name, size_t size = 10)
: name_(name), data_(std::make_unique<int[]>(size)), size_(size) {
std::fill(data_.get(), data_.get() + size_, 0);
}
// 拷贝构造函数(删除)
ModernClass(const ModernClass&) = delete;
// 拷贝赋值运算符(删除)
ModernClass& operator=(const ModernClass&) = delete;
// 移动构造函数(默认)
ModernClass(ModernClass&&) noexcept = default;
// 移动赋值运算符(默认)
ModernClass& operator=(ModernClass&&) noexcept = default;
// 析构函数(默认)
~ModernClass() = default;
// 自定义成员函数
void set_value(size_t index, int value) {
if (index < size_) {
data_[index] = value;
}
}
int get_value(size_t index) const {
return (index < size_) ? data_[index] : 0;
}
const std::string& name() const { return name_; }
size_t size() const { return size_; }
// 运算符重载
ModernClass operator+(const ModernClass& other) const {
size_t new_size = std::max(size_, other.size_);
ModernClass result(name_ + "+" + other.name_, new_size);
for (size_t i = 0; i < new_size; ++i) {
int val1 = (i < size_) ? data_[i] : 0;
int val2 = (i < other.size_) ? other.data_[i] : 0;
result.set_value(i, val1 + val2);
}
return result;
}
};
// 2. 委托构造函数
class Configuration {
private:
std::string host_;
int port_;
int timeout_;
bool use_ssl_;
public:
// 委托构造函数
Configuration() : Configuration("localhost", 80, 30, false) {}
Configuration(const std::string& host) : Configuration(host, 80, 30, false) {}
Configuration(const std::string& host, int port)
: Configuration(host, port, 30, false) {}
// 目标构造函数
Configuration(const std::string& host, int port, int timeout, bool use_ssl)
: host_(host), port_(port), timeout_(timeout), use_ssl_(use_ssl) {
std::cout << "创建配置: " << host_ << ":" << port_ << std::endl;
}
void print() const {
std::cout << "配置详情:" << std::endl;
std::cout << " 主机: " << host_ << std::endl;
std::cout << " 端口: " << port_ << std::endl;
std::cout << " 超时: " << timeout_ << "秒" << std::endl;
std::cout << " SSL: " << (use_ssl_ ? "启用" : "禁用") << std::endl;
}
};
// 3. 显式构造函数
class StringWrapper {
private:
std::string data_;
public:
// 允许隐式转换
StringWrapper(const char* str) : data_(str) {}
// 显式构造函数:禁止隐式转换
explicit StringWrapper(int length) : data_(length, ' ') {}
const std::string& get() const { return data_; }
};
void process_string(const StringWrapper& str) {
std::cout << "处理字符串: " << str.get() << std::endl;
}
// 4. final类和方法
class BaseClass {
public:
virtual void method1() {
std::cout << "BaseClass::method1" << std::endl;
}
// final虚函数:不能被子类重写
virtual void method2() final {
std::cout << "BaseClass::method2 (final)" << std::endl;
}
};
class DerivedClass final : public BaseClass { // final类:不能被继承
public:
void method1() override {
std::cout << "DerivedClass::method1" << std::endl;
}
// 不能重写method2,因为它是final的
// void method2() override { ... } // 错误
};
// 5. override关键字
class Shape {
public:
virtual ~Shape() = default;
virtual void draw() const {
std::cout << "绘制形状" << std::endl;
}
virtual double area() const = 0;
// 重载虚函数
virtual void scale(double factor) {
std::cout << "缩放: " << factor << std::endl;
}
virtual void scale(double x, double y) {
std::cout << "缩放: (" << x << ", " << y << ")" << std::endl;
}
};
class Circle : public Shape {
private:
double radius_;
public:
explicit Circle(double radius) : radius_(radius) {}
// 使用override确保正确重写
void draw() const override {
std::cout << "绘制圆形,半径: " << radius_ << std::endl;
}
double area() const override {
return 3.141592653589793 * radius_ * radius_;
}
// 重写一个重载版本
void scale(double factor) override {
radius_ *= factor;
std::cout << "圆形缩放后半径: " << radius_ << std::endl;
}
// 可以添加新函数
double circumference() const {
return 2 * 3.141592653589793 * radius_;
}
};
// 6. 类内成员初始化
class GameCharacter {
private:
// 类内成员初始化(C++11)
std::string name_ = "未知角色";
int health_ = 100;
int level_ = 1;
double experience_ = 0.0;
// const成员也可以在类内初始化
const int max_health_ = 1000;
// 静态成员类内初始化(C++17)
inline static int total_characters_ = 0;
public:
// 委托构造函数可以使用类内初始值
GameCharacter() {
total_characters_++;
}
GameCharacter(const std::string& name) : name_(name) {
total_characters_++;
}
GameCharacter(const std::string& name, int health, int level)
: name_(name), health_(health), level_(level) {
total_characters_++;
}
~GameCharacter() {
total_characters_--;
}
void print() const {
std::cout << "角色信息:" << std::endl;
std::cout << " 名称: " << name_ << std::endl;
std::cout << " 生命值: " << health_ << "/" << max_health_ << std::endl;
std::cout << " 等级: " << level_ << std::endl;
std::cout << " 经验: " << experience_ << std::endl;
}
static int get_total_characters() {
return total_characters_;
}
};
// 7. 结构化绑定(C++17)
class Point3D {
private:
double x_, y_, z_;
public:
Point3D(double x, double y, double z) : x_(x), y_(y), z_(z) {}
// 使类支持结构化绑定
template<size_t Index>
auto get() const {
if constexpr (Index == 0) return x_;
else if constexpr (Index == 1) return y_;
else if constexpr (Index == 2) return z_;
}
double x() const { return x_; }
double y() const { return y_; }
double z() const { return z_; }
};
// 为Point3D特化std::tuple_size和std::tuple_element
namespace std {
template<>
struct tuple_size<Point3D> : integral_constant<size_t, 3> {};
template<size_t Index>
struct tuple_element<Index, Point3D> {
using type = double;
};
}
// 8. 三路比较运算符(C++20)
#include <compare> // 需要C++20
class ModernVersion {
private:
int major_, minor_, patch_;
public:
ModernVersion(int major = 1, int minor = 0, int patch = 0)
: major_(major), minor_(minor), patch_(patch) {}
// 三路比较运算符(C++20)
auto operator<=>(const ModernVersion& other) const = default;
// 或者自定义比较逻辑
/*
auto operator<=>(const ModernVersion& other) const {
if (auto cmp = major_ <=> other.major_; cmp != 0) return cmp;
if (auto cmp = minor_ <=> other.minor_; cmp != 0) return cmp;
return patch_ <=> other.patch_;
}
*/
void print() const {
std::cout << "v" << major_ << "." << minor_ << "." << patch_ << std::endl;
}
};
int main() {
std::cout << "=== 现代C++类特性 ===" << std::endl;
// 1. 默认和删除的特殊成员函数
std::cout << "\n1. 默认和删除函数:" << std::endl;
ModernClass obj1("对象1", 5);
ModernClass obj2("对象2", 3);
// 不能拷贝
// ModernClass obj3 = obj1; // 错误:拷贝构造函数已删除
// 可以移动
ModernClass obj3 = std::move(obj1);
std::cout << "移动后obj1大小: " << obj1.size() << std::endl;
std::cout << "obj3大小: " << obj3.size() << std::endl;
// 运算符重载
ModernClass sum = obj2 + obj3;
std::cout << "求和对象名称: " << sum.name() << std::endl;
// 2. 委托构造函数
std::cout << "\n2. 委托构造函数:" << std::endl;
Configuration cfg1;
Configuration cfg2("example.com");
Configuration cfg3("api.example.com", 443);
Configuration cfg4("secure.example.com", 8443, 60, true);
cfg1.print();
cfg2.print();
cfg3.print();
cfg4.print();
// 3. 显式构造函数
std::cout << "\n3. 显式构造函数:" << std::endl;
StringWrapper str1("Hello"); // 允许隐式转换
StringWrapper str2(10); // 显式构造函数
process_string(str1); // OK
process_string("World"); // OK:隐式转换
// process_string(20); // 错误:不能隐式转换int到StringWrapper
// 4. final类和方法
std::cout << "\n4. final类和方法:" << std::endl;
DerivedClass derived;
derived.method1();
derived.method2(); // 调用基类的final方法
// 5. override关键字
std::cout << "\n5. override关键字:" << std::endl;
Circle circle(5.0);
circle.draw();
std::cout << "面积: " << circle.area() << std::endl;
std::cout << "周长: " << circle.circumference() << std::endl;
circle.scale(1.5);
// 6. 类内成员初始化
std::cout << "\n6. 类内成员初始化:" << std::endl;
GameCharacter char1;
GameCharacter char2("英雄");
GameCharacter char3("BOSS", 500, 10);
char1.print();
char2.print();
char3.print();
std::cout << "总角色数: " << GameCharacter::get_total_characters() << std::endl;
// 7. 结构化绑定(C++17)
std::cout << "\n7. 结构化绑定:" << std::endl;
Point3D point(1.0, 2.0, 3.0);
auto [x, y, z] = point; // 结构化绑定
std::cout << "点坐标: (" << x << ", " << y << ", " << z << ")" << std::endl;
// 8. 三路比较运算符(C++20)
std::cout << "\n8. 三路比较运算符:" << std::endl;
ModernVersion v1(1, 2, 3);
ModernVersion v2(1, 2, 4);
ModernVersion v3(2, 0, 0);
std::cout << "v1: "; v1.print();
std::cout << "v2: "; v2.print();
std::cout << "v3: "; v3.print();
// 使用三路比较
#if __cplusplus >= 202002L
std::cout << "v1 < v2: " << ((v1 < v2) ? "true" : "false") << std::endl;
std::cout << "v1 <= v2: " << ((v1 <= v2) ? "true" : "false") << std::endl;
std::cout << "v1 > v3: " << ((v1 > v3) ? "true" : "false") << std::endl;
std::cout << "v1 == v2: " << ((v1 == v2) ? "true" : "false") << std::endl;
std::cout << "v1 != v3: " << ((v1 != v3) ? "true" : "false") << std::endl;
#endif
return 0;
}
总结
通过本文的详细探讨,我们深入了解了C++如何解决C语言在大型项目开发中的核心痛点:
1. 命名空间(namespace)
- 解决了全局命名冲突问题
- 支持嵌套和别名,提供更好的模块化
- 是现代C++模块化的基础
2. 缺省参数
- 使函数调用更加灵活
- 减少了代码重复
- 需要遵循从右向左连续设置的规则
3. 引用(&)
- 提供了更安全、更直观的指针替代方案
- 必须初始化,不能重新绑定
- 语法更简洁,减少错误
4. const引用
- 提供了只读访问权限
- 可以绑定到临时对象
- 增强了类型安全和代码可读性
5. 指针和引用的区别
- 引用是别名,指针是存储地址的变量
- 引用更安全,指针更灵活
- 根据具体场景选择合适的方式
6. inline内联函数
- 替代了不安全的宏函数
- 提供了类型检查和调试支持
- 在性能关键代码中特别有用
7. nullptr
- 解决了NULL的类型歧义问题
- 提供了类型安全的空指针表示
- 是现代C++中空指针的最佳实践
8. 类(class)基础
- 引入了面向对象编程
- 提供了封装、继承和多态的支持
- 是现代软件工程的基础
学习建议:
- 循序渐进:从C语言基础开始,逐步掌握C++的新特性
- 实践为主:通过实际项目应用这些特性,加深理解
- 关注现代C++:学习C++11/14/17/20的新特性
- 阅读优秀代码:学习开源项目中的最佳实践
- 理解设计理念:不仅学习语法,更要理解C++的设计哲学
C++作为一门既支持底层系统编程又支持高级抽象的语言,在性能、灵活性和表达力之间取得了良好的平衡。掌握这些核心特性,你将能够编写出更安全、更高效、更易于维护的代码,为深入学习现代C++打下坚实的基础。