目录
在上一期的技术解析中,我们系统性地探讨了指针这一C++核心机制的内在逻辑,深入剖析了指针在内存管理、数据结构实现以及高效算法设计中的关键作用。作为承前启后的重要章节,在今天的深度解析中,我们将以更专业的视角继续推进C++知识体系的构建。本次探讨不仅将延续指针相关的高级话题,更将开启对C++现代编程范式的全面解读,通过精心设计的案例分析和底层原理图示,我们将共同解构这门经典语言的演进脉络,揭示其在高性能计算、系统编程等关键领域的独特优势,帮助您掌握C++编程的艺术,在软件工程的实践中实现从语法认知到工程思维的跨越式提升。
函数是完成特定功能的独立代码模块,程序代码独立。
(存储类型) 数据类型 函数名(形式参数)
{
函数体;
return 常量、变量或表达式;
}
补充说明:
static
表示静态函数(仅当前文件可见)。void
表示无返回值。不接收返回值
函数名(实参);
接收返回值
数据类型 变量名 = 函数名(实参);
示例:
#include
using namespace std;
void print() {
cout << "hello world!" << endl;
}
// 函数声明
int add(int a, int b);
int main() {
print(); // 不接收返回值
int sum = add(3, 4); // 接收返回值
cout << sum << endl;
return 0; // C++中可省略,默认返回0
}
// 函数定义
int add(int a, int b) {
return a + b;
}
示例(交换函数):
// 地址传递
void swap_ptr(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
// 引用传递
void swap_ref(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 3, y = 4;
swap_ptr(&x, &y); // 需取地址
swap_ref(x, y); // 直接传变量
cout << x << " " << y << endl; // 输出 4 3
return 0;
}
C风格字符串(需包含
):
strlen
:计算字符串长度(不含 \0
)。strcpy
:复制字符串(存在溢出风险)。strncpy
:安全版本,限制复制长度。strcat
:拼接字符串。strcmp
:按字典序比较字符串。#include
#include // 包含字符串操作函数头文件
using namespace std;
int main(void)
{
char buf[32] = "world"; // 声明并初始化32字节字符数组,注意字符串末尾自动添加'\0'
char str[32] = "world"; // 声明并初始化第二个32字节字符数组
cout << sizeof(buf) << endl; // 输出数组总大小:32(sizeof计算内存大小,包含未使用的空间)
cout << strlen(buf) << endl; // 输出字符串有效长度:5(strlen计算到'\0'前的字符数)
// 字符串操作示例(当前被注释)
// strcpy(buf, "world123"); // 字符串复制:将第二个参数复制到第一个参数指向的数组
// strcat(buf, "!"); // 字符串拼接:将第二个参数追加到第一个参数字符串末尾
// cout << buf << endl; // 输出操作后的buf内容
cout << strcmp(buf, str) << endl; // 字符串比较:按字典序比较
// 返回正数(buf>str),负数(buf
inline
)定义:
在函数定义前添加 inline
关键字,建议编译器将函数体直接展开到调用处,避免函数调用的开销(如压栈、跳转和返回操作)。
适用场景:
switch
)。getter/setter
)。注意事项:
inline
是建议性关键字,编译器可能忽略(如函数体过大或含递归)。示例:
// 头文件声明
inline int max(int a, int b) {
return a > b ? a : b;
}
overload
)规则:
示例:
// 演示函数重载的示例程序
// 两个整数相加的函数重载
// 参数:a - 第一个整数,b - 第二个整数
// 返回值:两个整数的和
int add(int a, int b)
{
return a + b;
}
// 三个整数相加的函数重载
// 参数:a - 第一个整数,b - 第二个整数,c - 第三个整数
// 返回值:三个整数的和
int add(int a, int b, int c)
{
return a + b + c;
}
// 两个双精度浮点数相加的函数重载
// 参数:c - 第一个双精度数,d - 第二个双精度数
// 返回值:两个双精度数的和
double add(double c, double d)
{
return c + d;
}
int main(void)
{
// 调用两个整数相加版本(参数类型和数量匹配)
cout << add(3, 4) << endl; // 输出 7
// 调用三个整数相加版本(参数数量匹配)
cout << add(3, 4, 5) << endl; // 输出 12
// 调用双精度版本(参数类型匹配,自动选择浮点数版本)
cout << add(1.23, 2.34) << endl; // 输出 3.57
return 0;
}
/*
代码特点说明:
1. 通过参数的数量和类型不同实现函数重载
2. 编译器根据调用时的参数自动选择匹配的函数版本
3. 演示了C++中不同类型和参数数量的函数重载方式
4. 浮点数版本使用不同的参数名(c,d)说明参数命名不影响重载解析
*/
注意事项:
避免隐式转换导致的歧义:
add(1, 2.5); // 歧义:可能调用 int add(int, int) 或 double add(double, double)
规则:
示例:
// 正确声明
int add(int a, int b = 1, int c = 2);
// 错误:非默认参数在默认参数后
int add(int a, int b = 1, int c);
// 二义性示例
void print(int x) {}
void print(int x, int y = 0) {}
print(10); // 错误:编译器无法确定调用哪个版本
高级特性:
多次声明可扩展默认参数(不可修改已存在的默认值):
void func(int a, int b, int c = 3);
void func(int a, int b = 2, int c); // 合法:扩展 b 的默认值
引用(Reference)是 C++ 中为变量起的一个别名,不独立开辟内存空间,而是共享被引用变量的内存地址。引用是 C++ 特有的语法特性,常用于函数参数传递、返回值优化等场景。
数据类型 &引用名 = 被引用变量; // 必须初始化!
示例:
int a = 10;i
nt &ref = a; // ref 是 a 的别名
必须初始化
引用在定义时必须绑定到一个变量,且之后不可更改绑定对象。
int x = 5;
int &r1 = x; // 正确
int &r2; // 错误:未初始化
通过引用修改变量
对引用的操作等同于直接操作被引用的变量。
int a = 10;
int &ref = a;
ref = 20; // a 的值变为 20
const 引用
int a = 10;
const int &b = a; // 不可通过 b 修改 a
a = 20; // 合法:通过原变量修改
const int &c = 100; // 合法:绑定到字面量
const int &d = a + 1; // 合法:绑定到表达式结果
const
修饰的引用不能通过引用修改值,但原变量仍可修改。const
)。引用的本质
底层通过指针实现,但语法上隐藏了指针操作,更安全直观。
#include
using namespace std;
int main() {
int a = 10;
const int &b = a; // const 引用
// b = 20; // 错误:不能通过 const 引用修改值
a = 20; // 合法:直接修改原变量
cout << a << " " << b << endl; // 输出 20 20
const int &c = 100; // 绑定到常量
cout << c << endl; // 输出 100
return 0;
}
区别点 | 指针 | 引用 |
---|---|---|
初始化 | 可以不初始化(但危险) | 必须初始化 |
空值 | 可指向 nullptr |
不能为空 |
重绑定 | 可修改指向的地址 | 一旦绑定,不可更改 |
内存占用 | 有自己的内存地址(存储指针值) | 无独立内存(与被引用变量共享地址) |
操作语法 | 需用 * 解引用 |
直接使用,无需特殊符号 |
多级间接访问 | 支持多级(如 int **p ) |
仅一级 |
安全性 | 可能悬空或越界 | 更安全(非空且类型严格匹配) |
函数参数 | 需显式传递地址 | 隐式传递地址,代码更简洁 |
关键总结
string
类详解#include
(必须显式包含,即使使用 using namespace std;
)char*
/char[]
),提供自动内存管理、丰富的成员函数和运算符重载。basic_string
的别名,动态分配内存,无需手动管理。string s1; // 空字符串
string s2 = "hello"; // 字面量初始化
string s3("world"); // 构造函数初始化
string s4(5, 'a'); // 生成 "aaaaa"
string s5(s2); // 拷贝构造
string s = "Hello";
s += " World"; // 运算符重载(推荐)
s.append("!"); // 追加字符串
s.push_back('!'); // 追加单个字符
s.insert(2, "xyz"); // 在索引2插入"xyz",原内容后移
s.size(); // 实际字符数(与 s.length() 等价)
s.empty(); // 判断是否为空(等价于 s.size() == 0)
s.capacity(); // 当前分配的内存容量
s.resize(10); // 调整字符串长度(截断或填充默认字符)
s.reserve(100); // 预分配内存,避免频繁扩容
s[5] = 'X'; // 快速访问(无边界检查,越界行为未定义)
s.at(5) = 'X'; // 带边界检查(越界抛出 std::out_of_range)
s.front(); // 首字符
s.back(); // 末字符
string sub = s.substr(3, 5); // 从索引3开始取5个字符
size_t pos = s.find("lo"); // 返回首次出现的位置(未找到返回 string::npos)
pos = s.rfind("l"); // 反向查找
s.replace(0, 5, "Hi"); // 替换前5个字符为"Hi"
if (s == "Hello") { ... } // 运算符重载比较(区分大小写)
int cmp = s.compare("Hi"); // 返回0(相等)、正数(s大)或负数
// 数值转换(C++11)
int num = stoi("123");
double d = stod("3.14");
string str = to_string(42);
s.clear(); // 清空内容(size=0,capacity不变)
s.swap(s2); // 高效交换两个字符串内容
const char* cstr = s.c_str(); // 获取C风格字符串(只读,以'\0'结尾)
char* data = s.data(); // 获取字符数组(C++17后可写,但需谨慎)
s = "New String"; // 直接赋值C字符串
operator[]
不检查边界,at()
会抛出异常。reserve()
预分配内存减少扩容开销。"Hello"s
后缀(需 using namespace std::string_literals;
),直接生成 string
对象。#include
#include
using namespace std;
int main() {
string s = "Hello";
s += " C++"; // s = "Hello C++"
s.insert(5, " Awesome"); // s = "Hello Awesome C++"
s.replace(6, 7, "Standard"); // s = "Hello Standard C++"
cout << "Length: " << s.size() << endl; // 输出 18
cout << "Substr: " << s.substr(6, 7) << endl; // "Standard"
if (s.find("C++") != string::npos) {
cout << "Found 'C++'" << endl;
}
return 0;
}
new 关键字
delete 关键字
定义
内存泄漏指程序未能正确释放已申请的堆内存,导致系统无法重新利用该内存区域,长期积累可能引发程序性能下降或崩溃。
常见原因
[分配] → [使用] → [释放]
↑ ↓
└─泄漏点───┘
// 基本用法
int main()
{
// 单个整型分配(带初始化)
int* ptr = new int(10); // 分配并初始化为10
cout << *ptr << endl;
delete ptr; // 正确释放
// 数组分配
char* str = new char[10];
strcpy(str, "hello");
cout << str << endl;
delete[] str; // 数组必须使用delete[]
// 安全指针处理
ptr = nullptr; // 释放后置空避免野指针
str = nullptr;
return 0;
}
提供逻辑分组机制,解决不同模块/库之间的命名冲突问题。尤其在以下场景至关重要:
namespace 名称 {
// 函数、变量、类、类型别名等声明/定义
// 支持嵌套定义(C++11起支持内联命名空间)
}
特性说明:
namespace短名 = 原长名字空间名;
using namespace 名称; // 引入整个名字空间(慎用)
using 名称::具体成员; // 仅引入特定成员(推荐方式)
显式限定(推荐用于关键代码)
名字空间::成员;
作用域限定符解析
::全局变量; // 访问全局作用域
名字空间::成员; // 访问指定名字空间
#include
// 全局变量
int a = 10;
namespace demo {
int a = 100; // 隐藏全局变量
int c = 30;
void print() {
std::cout << "Namespace function" << std::endl;
}
}
// using namespace demo; // 慎用全局引入
int main() {
// 访问全局变量
std::cout << "Global a: " << ::a << std::endl; // 10
// 完全限定访问
std::cout << "demo::a: " << demo::a << std::endl; // 100
// 局部使用声明
using demo::c;
std::cout << "Local c: " << c << std::endl; // 30
// 函数调用
demo::print();
return 0;
}
// 名字空间别名
namespace d = demo;
// 跨文件扩展(头文件中常用)
namespace demo {
void new_function();
}
// 匿名名字空间(替代static)
namespace {
int internal_var; // 文件内可见
}
特征 | 定义 | 作用 | 示例 |
---|---|---|---|
封装 | 将数据(属性)和操作数据的方法(行为)绑定为一个独立的对象,隐藏内部实现细节 | 提高安全性(数据保护)、简化外部调用、降低模块耦合性 | 学生对象 :封装姓名、成绩等属性,提供修改成绩() 方法 |
继承 | 子类继承父类的属性和方法,并可扩展或重写父类功能 | 实现代码复用、建立类层次结构、支持多态 | 动物类 派生出猫类 和狗类 ,复用eat() 方法并重写sound() 方法 |
多态 | 同一操作作用于不同对象时产生不同行为(静态多态、动态多态) | 提高代码灵活性、支持接口统一调用、增强系统扩展性 | 父类Shape 定义draw() ,子类Circle 和Square 各自实现不同的draw() |
维度 | 面向过程(Procedure-Oriented) | 面向对象(Object-Oriented) |
---|---|---|
程序结构 | 以函数为中心,程序由一系列顺序调用的函数组成 | 以对象为中心,程序由交互的对象构成 |
数据与行为关系 | 数据与函数分离,数据作为参数传递 | 数据和方法绑定在对象内部,通过对象操作数据 |
核心关注点 | “如何做”(How to do),聚焦具体执行步骤 | “谁来做”(Who does it),聚焦对象职责划分 |
代码复用 | 通过函数复用逻辑,但数据复用性差 | 通过继承、组合复用对象和代码 |
维护性 | 修改可能影响全局逻辑,维护成本高 | 对象独立性高,修改局部不影响整体 |
典型场景 | 简单算法、数学计算、一次性脚本 | 复杂系统、GUI开发、业务模型抽象 |
Book
类:封装书名、ISBN、借阅状态等属性,提供借阅()
、归还()
方法。User
类:封装用户信息,管理借阅记录。Library
类:聚合图书和用户,实现全局管理逻辑。面向过程实现:
def open_door(): ...
def put_elephant(): ...
def close_door():
open_door()
put_elephant()
close_door()
面向对象实现
class Refrigerator:
def open(self): ...
def store(self, obj): ...
def close(self): ...
fridge = Refrigerator()
elephant = Elephant()
fridge.open()
fridge.store(elephant)
fridge.close()
Refrigerator
对象封装行为,调用者无需关心冰箱内部机制。面向对象三大特性
├─ 封装(本节重点)
├─ 继承
└─ 多态
类成员类型
├─ 数据成员(属性)
├─ 成员函数(方法)
├─ 构造函数/析构函数
└─ 静态成员
类
品牌
、重量
),一般为名词。拨打电话
),一般为动词。对象
class 类名 {
访问权限:
成员变量;
成员函数(参数){...}
};
访问权限
权限 | 类内访问 | 类外访问 | 继承特性 |
---|---|---|---|
private | ✔ | ✘ | 默认权限,子类不可访问 |
protected | ✔ | ✘ | 子类可访问 |
public | ✔ | ✔ | 子类可访问 |
示例与注意事项
class MobilePhone {
private: // 成员变量通常设为私有(封装性)
float weight;
string brand; // 建议使用string替代char数组
public: // 成员函数通常设为公有(提供接口)
void call() {
cout << "拨打电话中..." << endl;
}
};
// 错误示例:外部直接访问私有成员
int main() {
MobilePhone mp;
// mp.weight = 3.45; // 编译错误!私有成员不可外部访问
mp.call(); // 公有方法可正常调用
return 0;
}
类型 | 生命周期管理 | 语法示例 | 内存分配区域 |
---|---|---|---|
栈内存对象 | 超出作用域自动销毁 | MobilePhone mp; |
栈空间 |
堆内存对象 | 需手动new/delete 管理 |
MobilePhone* p = new MobilePhone(); |
堆空间 |
代码示例
int main() {
// 栈对象(自动回收)
MobilePhone mp;
mp.call();
// 堆对象(需手动管理)
MobilePhone* p = new MobilePhone();
p->call();
delete p; // 避免内存泄漏
return 0;
}
方式一:类内直接初始化(C++11+)
class MobilePhone {
private:
float weight = 150.0; // 初始值
string brand = "Huawei";
};
方式二:构造函数初始化(推荐)
class MobilePhone {
private:
float weight;
string brand;
public:
// 构造函数
MobilePhone(float w, string b) : weight(w), brand(b) {} // 初始化列表
// Setter方法(可选)
void setBrand(string b) { brand = b; }
};
int main() {
MobilePhone mp(182.0, "Xiaomi"); // 通过构造函数初始化
return 0;
}
方式三:成员函数赋值
class MobilePhone {
public:
void initData(float w, string b) {
weight = w;
brand = b;
}
};
#include
#include
using namespace std;
/**
* 手机类
* 用于表示手机设备的基本属性和行为
*/
class MobilePhone{
private:
float weight; // 手机重量(单位由实现定义)
char brand[32]; // 品牌名称(最大存储31个字符+结束符)
char color; // 颜色标识(使用单个字符表示,如'r'=红色)
public:
/**
* 模拟打电话功能
*/
void callTel(){
cout << "打电话" << endl;
}
/**
* 设置手机属性值
* @param w 重量值
* @param b 品牌字符串(需保证长度不超过31字符)
* @param c 颜色标识字符
*/
void setValue(float w, char *b, char c){
weight = w;
strcpy(brand, b); // 注意:存在缓冲区溢出风险,需保证b长度合法
color = c;
}
/**
* 输出手机属性信息
* 注意:当前实现不显示颜色信息
*/
void getValue(){
cout << "weight:" << weight << endl;
cout << "brand:" << brand << endl;
}
};
int main(void)
{
MobilePhone mp; // 创建手机实例
mp.setValue(12.5, "huawei", 'b'); // 设置属性(重量12.5,品牌华为,颜色'b')
mp.getValue(); // 输出属性信息
return 0;
}
构造函数是类的特殊成员函数,在对象创建时自动调用,用于初始化类的数据成员。
特点:
void
)类名(参数列表) {
// 初始化逻辑
}
示例:
class Example {
public:
Example() {} // 默认构造
Example(int a, int b) {} // 带参构造
};
自动生成规则
重载与默认参数
MobilePhone(float w, string b, char c = 'b') { /*...*/ }
初始化职责
特殊成员初始化
const
成员、引用成员或类类型成员(无默认构造),必须使用成员初始化列表。示例
/*
* 移动电话类 - 描述手机基本属性和功能
*/
class MobilePhone {
private:
float weight; // 手机重量(单位未指定,假设为克)
string brand; // 手机品牌(如xiaomi, huawei)
char color; // 颜色标识('b'=黑色,'w'=白色等,默认黑色)
public:
/**
* 构造函数(支持默认参数)
* @param w 手机重量
* @param b 品牌名称
* @param c 颜色标识(默认值'b'表示黑色)
*/
MobilePhone(float w, string b, char c = 'b') {
weight = w;
brand = b;
color = c;
}
// 以下是被注释掉的构造函数重载示例(参数列表不同)
// MobilePhone(float w, string b) {
// weight = w;
// brand = b;
// color = 'b'; // 不指定时也默认黑色
// }
/// 模拟打电话功能
void callTel() {
cout << "打电话" << endl;
}
/// 打印手机属性信息
void getValue() {
cout << "weight:" << weight << endl;
cout << "brand:" << brand << endl;
cout << "color:" << color << endl;
}
};
int main(void)
{
// 创建手机对象(显式指定所有参数)
MobilePhone mp(10, "xiaomi", 'w'); // 白色小米手机
// 创建手机对象(使用color默认参数)
MobilePhone mp1(20, "huawei"); // 黑色华为手机(默认颜色)
// 输出两个手机的信息
mp.getValue();
mp1.getValue();
return 0;
}
类名(参数列表) : 成员1(值1), 成员2(值2) { /*...*/ }
示例:
MobilePhone(float w, string b, char c) : weight(w), brand(b), color(c) {}
MobilePhone mp = 200.5; // 隐式调用 MobilePhone(float w)
explicit
关键字。 explicit MobilePhone(float w) : weight(w) {}
示例
/*
* 这是一个演示 explicit 构造函数作用的示例程序
*/
#include
using namespace std;
class MobilePhone {
private:
float weight; // 手机重量属性
public:
// explicit 关键字禁止隐式类型转换
// 只能通过显式调用来创建对象
explicit MobilePhone(float w) : weight(w) {
cout << "creater" << endl; // 对象创建时输出提示信息
}
void getValue() {
cout << "weight:" << weight << endl; // 输出重量信息
}
};
int main(void)
{
// 错误示例:尝试隐式转换(编译将失败)
// explicit构造函数禁止这种形式的初始化
// MobilePhone mp1 = 200.5; // 错误:不能将float隐式转换为MobilePhone对象
// 正确用法1:显式调用构造函数
MobilePhone mp1(200.5);
// 正确用法2:使用拷贝初始化语法(实际仍是显式构造)
// MobilePhone mp1 = MobilePhone(200.5);
mp1.getValue();
return 0;
}
/*
* 关键点解释:
* 1. explicit关键字的作用:
* - 只能用于单参数的构造函数
* - 防止编译器进行隐式的类型转换
* - 要求必须显式调用构造函数
*
* 2. 错误行分析:
* MobilePhone mp1 = 200.5 实际上尝试执行:
* MobilePhone mp1 = MobilePhone(200.5)
* 但由于explicit存在,这种隐式转换会被阻止
*
* 3. 解决方法:
* - 使用直接初始化语法:MobilePhone mp1(200.5)
* - 或者显式构造临时对象:MobilePhone mp1 = MobilePhone(200.5)
*/
类名(const 类名& other)
。代码示例
#include
using namespace std;
// 定义手机类
class MobilePhone{
private:
float weight; // 私有成员变量,表示手机的重量(单位:克)
public:
// 构造函数,使用初始化列表初始化weight
MobilePhone(float w):weight(w){
cout << "creater" << endl; // 对象创建时输出构造提示
}
// 拷贝构造函数(参数应为const引用更规范,此处保留原始实现)
MobilePhone(MobilePhone &mp){ // 通过现有对象创建新对象
cout << "copy creater" << endl; // 拷贝构造时输出提示
weight = mp.weight; // 复制重量属性
}
// 成员函数:显示手机重量信息
void getValue(){
cout << "weight:" << weight << endl;
}
};
int main(void)
{
MobilePhone mp1(10.5); // 通过普通构造函数创建对象(输出creater)
MobilePhone mp2(mp1); // 通过拷贝构造函数创建对象(输出copy creater)
mp1.getValue(); // 显示第一个对象的重量(10.5)
mp2.getValue(); // 显示第二个对象的重量(同样为10.5)
return 0;
}
类型 | 行为 | 适用场景 |
---|---|---|
浅拷贝 | 复制指针值(共享同一内存) | 无动态资源的简单对象 |
深拷贝 | 复制指针指向的内容(独立内存) | 含动态资源(如堆内存) |
浅拷贝风险:
mp2.setValue()
影响 mp1
)。示例
/*
* 内容: 演示深拷贝与浅拷贝区别的移动电话类实现
*/
#include
#include // 用于字符串操作函数
using namespace std;
// 移动电话类
class MobilePhone {
private:
float weight; // 手机重量(单位:克)
char *brand; // 手机品牌(使用动态内存分配)
public:
// 参数化构造函数
MobilePhone(float w, char *b) : weight(w) {
// 为brand分配足够的内存(包含空终止符)
brand = new char[strlen(b) + 1];
// 深拷贝品牌字符串
strcpy(brand, b);
}
// 拷贝构造函数
MobilePhone(MobilePhone &mp) {
cout << "copy creator" << endl;
weight = mp.weight;
// 深拷贝实现:
// 1. 分配新内存
// 2. 复制字符串内容
brand = new char[strlen(mp.brand) + 1];
strcpy(brand, mp.brand);
/* 危险!浅拷贝实现(已注释)
* 会导致两个对象共享同一内存地址
* 可能引发双重释放或数据篡改
*/
// brand = mp.brand;
}
// 注意:类缺少析构函数!
// 建议添加:~MobilePhone() { delete[] brand; }
// 修改品牌值的方法
void setValue() {
// 注意:存在潜在缓冲区溢出风险
// 建议:使用strncpy或动态调整内存大小
strcpy(brand, "huawei");
}
// 显示手机属性
void getValue() {
cout << "weight: " << weight << endl;
cout << "brand: " << brand << endl;
}
};
int main(void) {
char buf[] = "xiaomi"; // 初始品牌
// 创建原始对象
MobilePhone mp1(10.5, buf);
// 通过拷贝构造函数创建新对象
MobilePhone mp2(mp1); // 此时会调用深拷贝
mp2.setValue(); // 修改拷贝对象的品牌
// 验证深拷贝效果:
mp1.getValue(); // 预期输出 xiaomi(原始品牌)
mp2.getValue(); // 预期输出 huawei(修改后品牌)
return 0;
}
/*
* 潜在问题:
* 1. 内存泄漏:未实现析构函数释放brand内存
* 2. setValue()存在缓冲区溢出风险
* 3. 建议使用std::string替代char*以简化内存管理
*/
十、new
、delete
与 malloc
、free的区别
new
/delete
new int
),返回具体类型的指针(无需类型转换)。malloc
/free
sizeof(int)
),返回 void*
(需显式类型转换)。new
:调用对象的构造函数(初始化对象)。delete
:调用对象的析构函数(清理资源)。malloc
/free
:仅分配/释放原始内存,不处理构造和析构。new
从自由存储区(free store)分配内存。malloc
从堆(heap)分配内存。
new
失败时抛出 std::bad_alloc
异常(可用 nothrow
版本返回 nullptr
)。malloc
失败时返回 NULL
,需手动检查返回值。malloc
可通过 realloc
调整内存块大小。new
无直接扩展内存的机制,需手动分配新内存并拷贝数据。new
/delete
可被重载,支持自定义内存管理策略。malloc
/free
不可重载。new
/delete
(确保构造/析构正确调用)。malloc
/free
,但不建议(可能导致未定义行为)。// C++ 风格(推荐)
int* p1 = new int(10); // 分配并初始化 int
delete p1; // 释放并调用析构函数(若有)
// C 风格(不推荐用于对象)
int* p2 = (int*)malloc(sizeof(int));
free(p2);
// 错误示例:混用
int* p3 = (int*)malloc(sizeof(int));
delete p3; // 未定义行为(未调用构造函数,但调用了析构函数)
new /delete |
malloc /free |
|
---|---|---|
语言 | C++ | C |
类型安全 | 是 | 否 |
构造/析构 | 自动调用 | 不处理 |
错误处理 | 异常 | 返回 NULL |
内存调整 | 不支持 | realloc |
重载 | 支持 | 不支持 |
始终在 C++ 中对对象使用 new
/delete
,避免混用!
本文系统解析C++编程核心知识,涵盖函数定义与传参方式(值传递、地址传递、引用传递)、字符串操作函数及C++特性(内联函数、函数重载、默认参数)。深入探讨引用变量本质及其与指针的差异,详解string类的动态内存管理与操作。对比new/delete与malloc/free的内存管理机制,剖析内存泄漏成因与防范。阐述名字空间的作用及面向对象三大特征(封装、继承、多态),结合类与对象设计实践,解析构造函数(含深/浅拷贝)、成员初始化列表等关键机制,最终通过代码示例展现C++高效、安全的编程范式。