林锐C/C++高质量编程指南之二
只是记了一部分我认为比较难理解的,或常用的,部分掌握的就没有写。
第七章内存管理
【规则 7-2-1】用 malloc 或 new 申请内存之后,应该立即检查指针值是否为 NULL。 防止使用指针值为 NULL 的内存。
【规则 7-2-2】不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右 值使用。
【规则 7-2-3】避免数组或指针的下标越界,特别要当心发生“多 1”或者“少 1” 操作。
【规则 7-2-4】动态内存的申请与释放必须配对,防止内存泄漏。
【规则 7-2-5】用 free 或 delete 释放了内存之后,立即将指针设置为 NULL,防止产 生“野指针”
以字符串为例比较指针与数组的特性。
示例 7-3-1 中,字符数组 a 的容量是 6 个字符,其内容为 hello 。a 的内容可以改变, 如 a[0]= ‘X’。
指针 p 指向常量字符串“world”(位于静态存储区,内容为 world ),常 量字符串的内容是不可以被修改的。
从语法上看,编译器并不觉得语句 p[0]= ‘X’有什么 不妥,但是该语句企图修改常量字符串的内容而导致运行错误
char a[] = "hello";
a[0] = 'X';
cout << a << endl;
char *p = "world"; // 注意 p 指向常量字符串
p[0] = 'X'; // 编译器不能发现该错误
cout << p << endl;
内容复制与比较
计算内存容量:
char a[] = "hello world";
char *p = a;
cout<< sizeof(a) << endl; // 12 字节
cout<< sizeof(p) << endl; // 4 字节void Func(char a[100]) { cout<< sizeof(a) << endl; // 4 字节而不是 100 字节 }
7.4指针参数是如何传递内存的
看下面的几个例子, 好多笔试题遇见过这几个例子
例一:
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL; GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
请问运行 Test 函数会有什么样的结果? 答:程序崩溃。 因为 GetMemory 并不能传递动态内存, Test 函数中的 str 一直都是 NULL。 strcpy(str, "hello world");将使程序崩溃。
例二:
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
请问运行 Test 函数会有什么样的结果?
答: (1)能够输出 hello
(2)内存泄漏 (没有加free函数)
例三:
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
请问运行 Test 函数会有什么样的结果?
答:可能是乱码。
因为 GetMemory 返回的是指向“栈内存” 的指针,
该指针的地址不是 NULL,但其原 现的内容已经被清除,新内容不可知
例四:
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if (str != NULL)
{
strcpy(str, “world”);
printf(str);
}
}
请问运行 Test 函数会有什么样的结果?
答:篡改动态内存区的内容,后果难以预料,非常危险。
因为 free(str);之后,str 成为野指针, if(str != NULL)语句不起作用。
7.6:动态分配的内存会自动释放吗?
void GetMemory(char *p)
{
p = (char *)malloc(100); // 动态内存会自动释放吗
}
我们发现指针有一些“似是而非”的特征:
(1)指针消亡了,并不表示它所指的内存会被自动释放。
(2)内存被释放了,并不表示指针会消亡或者成了 NULL 指针。
7.7野指针
“野指针”不是 NULL 指针,是指向“垃圾”内存的指针
造成野指针出现的原因:
(1)指针变量没有被初始化。例如:char p; 这个就是野指针,应该修改为char p = NULL;
(2)指针 p 被 free 或者 delete 之后,没有置为 NULL
7.11new和delete的使用要点:
int *p1 = (int *)malloc(sizeof(int) * length);
int *p2 = new int[length];
delete p2;
如果用 new 创建对象数组,那么只能使用对象的无参数构造函数。
Obj *objects = new Obj[100]; // 创建 100 个动态对象
在用 delete 释放对象数组时,留意不要丢了符号‘[]’。例如
delete []objects; // 正确的用法
delete objects; // 错误的用法
后者相当于 delete objects[0],漏掉了另外 99 个对象。
第八章:C++的高级特性
如果 C++程序要调用已经被编译后的 C 函数
假设某个 C 函数的声明如下:
void foo(int x, int y);
该函数被 C 编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int 之类的名字用来支持函数重载和类型安全连接。
由于编译后的名字不同,C++程序不能 直接调用 C 函数。
C++提供了一个 C 连接交换指定符号 extern“C”来解决这个问题。
extern “C”
{
void foo(int x, int y);
… // 其它函数
}或者:
extern “C”{ #include “myheader.h” … // 其它 C 头文件}
这就告诉 C++编译译器,函数 foo 是个 C 连接,应该到库中找名字_foo 而不是找 _foo_int_int。
C++编译器开发商已经对 C 标准库的头文件作了 extern“C”处理,所以我 们可以用#include 直接引用这些头文件
注意并不是两个函数的名字相同就能构成重载。全局函数和类的成员函数同名不算 重载,因为函数的作用域不同
调用时要加上作用域运算符
第九章:类的构造函数,析构函数,赋值函数
class String
{
public:
String(const char *str = NULL); // 普通构造函数
String(const String &other); // 拷贝构造函数
~ String(void); // 析构函数
String &operator =(const String &other); // 赋值函数
private:
char *m_data; // 用于保存字符串
};
构造函数的列表初始化:
如果类存在继承关系,派生类必须在其初始化表里调用基类的构造函数。
拷贝构造函数和赋值函数,当涉及到默认的含有内存操作时,要注意。
。以类 String 的两个对象 a,b 为例,假设 a.m_data 的内容为“hello”, b.m_data 的内容为“world”。
现将 a 赋给 b,缺省赋值函数的“位拷贝”意味着执行 b.m_data = a.m_data。
这将造成三个错误:
一是 b.m_data 原有的内存没被释放,造成内存泄露;
二是 b.m_data 和 a.m_data 指向同一块内存,a 或 b 任何一方变动都会影响另一方;
三是 在对象被析构时,m_data 被释放了两次。
string类的成员函数实现:
// String 的普通构造函数 String::String(const char *str)
{
if (str == NULL)
{
m_data = new char[1];
*m_data = ‘0’;
}
else
{
int length = strlen(str);
m_data = new char[length + 1];
strcpy(m_data, str);
}
}
// String 的析构函数
String::~String(void)
{
delete[] m_data; // 由于 m_data 是内部数据类型,也可以写成 delete m_data;
}
拷贝构造和赋值函数
// 拷贝构造函数
String::String(const String &other)
{
// 允许操作 other 的私有成员 m_data
int length = strlen(other.m_data);
m_data = new char[length + 1];
strcpy(m_data, other.m_data);
}
// 赋值函数
String &String::operate =(const String &other)
{
// (1) 检查自赋值
if(this == &other)
return *this;
// (2) 释放原有的内存资源
delete []m_data;
// (3)分配新的内存资源,并复制内容
int length = strlen(other.m_data);
m_data = new char[length + 1];
strcpy(m_data, other.m_data);
// (4)返回本对象的引用
return *this;
}
课后题及答案
https://www.cnblogs.com/xiaokang01/p/12718592.html
原文链接: https://www.cnblogs.com/xiaokang01/p/12716736.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/196118
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!