二义性
通过virtual关键字实现虚继承,可以解决多重继承造成的二义性问题
#include <iostream>
using namespace std;
class Animal {
public:
int m_Age;
};
class Lion : public Animal {};
class Tiger : public Animal {};
class LionTiger : public Lion, public Tiger {};
class LionV : virtual public Animal {};
class TigerV : virtual public Animal {};
class LionTigerV : public LionV, public TigerV {};
void test() {
LionTiger lt; // LionTiger 一个对象有两份年龄,不合常理
lt.Lion::m_Age = 1;
lt.Tiger::m_Age = 2;
cout << lt.Lion::m_Age << endl;
cout << lt.Tiger::m_Age << endl;
// cout << lt.m_Age << endl; // 强行把两份年龄当成一份去使用,就会引发二义性
LionTigerV ltv; // LionTigerV 一个对象有一份年龄,非常合理
ltv.m_Age = 3;
cout << ltv.m_Age << endl;
}
int main() {
test();
return 0;
}
二次析构
通过深拷贝解决二次析构问题
#include <iostream>
using namespace std;
class Person {
public:
Person(int age, int height) {
m_age = age;
m_height = new int(height);
}
Person(const Person& p) {
m_age = p.m_age;
m_height = new int(*p.m_height); // 深拷贝,p1 p2 两个对象的 m_height 指针不会指向堆区的同一块内存
}
~Person() {
if (nullptr != m_height) {
cout << "delete addr " << m_height << endl;
delete m_height; // 两次析构,不会重复释放堆区的同一块内存
}
}
private:
int m_age;
int* m_height;
};
void test() {
Person p1(18, 180);
Person p2(p1);
}
int main() {
test();
return 0;
}
扩展:为什么二次析构有时候会报错,有时候不会报错? 第一次析构掉内存没有问题,第二次析构的时候分两种情况: 如果这个内存已经被分配给其他进程:就会报错 如果这个内存还没被分配给其他进程:就不会报错
内存溢出
使用安全函数解决内存溢出问题
#include <iostream>
#include <cstring>
using namespace std;
int test() {
// 测试环境:小端机,栈上变量自高地址向低地址生长
char buf1[4];
char buf2[4];
char buf3[4];
strcpy(buf2, "BBBB");
strcpy(buf3, "CCCC");
cout << buf2 << endl; // buf2 的 '\0' 被 buf3 覆盖了
strcpy(buf1, "AAAA");
cout << buf2 << endl; // buf2 的首地址被 '\0' 覆盖了
return 0;
}
int main() {
test();
return 0;
}
子类内存泄漏
通过虚继承解决子类内存泄漏问题
#include <iostream>
using namespace std;
class Dad {
public:
~Dad() { cout << "Dad::destrutor" << endl; }
};
class Son : public Dad {
public:
~Son() { cout << "Son::destrutor" << endl; }
};
class Dad2 {
public:
virtual ~Dad2() { cout << "Dad2::destrutor" << endl; } // 虚析构
};
class Son2 : public Dad2 {
public:
~Son2() { cout << "Son2::destrutor" << endl; }
};
int main()
{
Dad* p = new Son;
delete p; // 从打印结果可以看出,子类 Son 的析构函数没有被调用
cout << endl;
Dad2* q = new Son2;
delete q;
return 0;
}
整型溢出
通过位运算解决整型溢出问题
#include <iostream>
using namespace std;
int main() {
int l = 2147483644, r = 2147483646;
int m1 = l + ((r - l) >> 1);
cout << "correct mid: " << m1 << endl;
int m2 = (l + r) / 2;
cout << "wrong mid: " << m2 << endl;
return 0;
}
访问非法内存
通过静态扫描工具,可以有效避免访问非法内存
#include <iostream>
using namespace std;
class Person {
public:
Person() {
var_ = 0;
}
void func_1() {
cout << "func_1" << endl;
}
void func_2() {
cout << var_ << endl;
cout << "func_2" << endl;
}
virtual void func_3() {};
private:
int var_;
};
void test() {
Person *p = nullptr;
cout << p << endl; // 不会报错:因为空指针在内存里地址是0
p->func_1(); // 不会报错:
p->func_2(); // 会报错:访问了成员变量,地址不存在
p->func_3(); // 会报错:访问了虚指针,地址不存在
}
int main() {
test();
return 0;
}
扩展1:常见的非法内存包括: 1、受保护的0地址内存 2、进程空间外的内存 3、未申请/已回收的内存
扩展2:常见的访问非法内存行为包括 1、访问数组越界 2、使用空指针成员 3、操作野指针、空悬指针
析构函数调用 delete this
todo
堆区申请内存忘记释放
todo
死锁问题
todo
递归锁问题
todo
线程joinable导致其他线程中途退出
todo
线程不可重入
todo
信号不可重入
todo
僵尸进程
todo
孤儿进程
todo
|