内存管理
内存泄漏
内存泄漏的严重性和付出的努力无关,只与使用的方法有关
关键
- 在更易管理的类型中隐藏分配和释放
- 单个对象
- 使用make_unique 或 make_shared
- 多个对象
- 使用标准容器
- vector、无序map
- 对字符串来说,使用string,比自己操作内存好
- 使用标准容器
- 单个对象
- 减少显式分配和解除分配的数量,使剩余的示例更容易跟踪。
- 缺少显式内存管理,宏,强制转换,溢出检查,显式大小限制和指针
- 如果您无法在应用程序中隐式处理分配/释放作为对象的一部分,则可以使用资源句柄来最小化泄漏的可能性
禁止
delete 已经delete 的内存
new/delete 和 malloc/free混用
在new 的指针上使用realloc
new、malloc区别
首先考虑使用make_unique 管理内存
malloc
- 参数为一个内存大小,返回void* ,数据未被初始化
- 通过返回0 来表示内存申请失败
new
- 参数为一个类型,加数量,加可选的,初始化参数,返回可选的初始化了的内存
- 通过抛出bad_alloc 来报告申请失败
cpp 中更推荐使用new,而不是malloc
原因
- 构造函数/析构函数
- 类型安全
- 重载
- new 操作可以被重载
cpp 中为什么没有类似realloc 的功能
使用标准容器,比如vector来让其增长是更好的选择
当然可以使用realloc,那么就舍弃了new 操作带来的所有好处,只取了realloc 带来的好处
我是否应该在new 后,检查返回的是否为NULL
不需要
- 如果当前没有内存可以使用了, 强制抛出异常
- force the new operator to throw an exception if it runs out of memory
- https://isocpp.org/wiki/faq/freestore-mgmt#making-new-check-for-null
auto p = make_unique(); // No need to check if p is null
相应的,delete 之前也不需要检查
- 为null什么都不会做
new 和 delete 的使用细节
如何将老代码转换为自动检查new 是否返回null
#include <new> // To get std::set_new_handler
#include <cstdlib> // To get abort()
#include <iostream> // To get std::cerr
class alloc_error : public std::exception {
public:
alloc_error() : exception() { }
};
void myNewHandler()
{
// This is your own handler. It can do anything you want.
throw alloc_error();
}
int main()
{
std::set_new_handler(myNewHandler); // Install your "new handler"
// ...
}
- 一些命名空间范围/全局/静态对象的构建函数使用了new,可能不会调用到,因为它们比main 先执行
new
- p = new Fred()中,如果Fred的构造函数抛出异常,是否会产生内存泄漏
- 不会
// Original code: Fred* p = new Fred();
Fred* p;
void* tmp = operator new(sizeof(Fred));
try {
new(tmp) Fred(); // Placement new
p = (Fred*)tmp; // The pointer is assigned only if the ctor succeeds
}
catch (...) {
operator delete(tmp); // Deallocate the memory
throw; // Re-throw the exception
}
delete
- delete 时的两个步骤
// Original code: delete p;
if (p) { // or "if (p != nullptr)"
p->~Fred();
operator delete(p);
}
- 成员函数delete this是否合法
- 首先确保自己是被new出来的,排出new []
- 此函数后不会再对此对象进行操作
- 确保成员函数的其它部分不会触及此对象的任何部分
- 子主题 4
如何强制令我的类只能通过new 生成
class Fred {
public:
// The create() methods are the "named constructors":
static Fred* create() { return new Fred(); }
static Fred* create(int i) { return new Fred(i); }
static Fred* create(const Fred& fred) { return new Fred(fred); }
// ...
private:
// The constructors themselves are private or protected:
Fred();
Fred(int i);
Fred(const Fred& fred);
// ...
};
友元函数可以替代这里的静态成员函数
简单的引用计数实现
class FredPtr;
class Fred {
public:
Fred() : count_(0) /*...*/ { } // All ctors set count_ to 0 !
// ...
private:
friend class FredPtr; // A friend class
unsigned count_;
// count_ must be initialized to 0 by all constructors
// count_ is the number of FredPtr objects that point at this
};
class FredPtr {
public:
Fred* operator-> () { return p_; }
Fred& operator* () { return *p_; }
FredPtr(Fred* p) : p_(p) { ++p_->count_; } // p must not be null
~FredPtr() { if (--p_->count_ == 0) delete p_; }
FredPtr(const FredPtr& p) : p_(p.p_) { ++p_->count_; }
FredPtr& operator= (const FredPtr& p)
{ // DO NOT CHANGE THE ORDER OF THESE STATEMENTS!
// (This order properly handles self-assignment)
// (This order also properly handles recursion, e.g., if a Fred contains FredPtrs)
Fred* const old = p_;
p_ = p.p_;
++p_->count_;
if (--old->count_ == 0) delete old;
return *this;
}
private:
Fred* p_; // p_ is never NULL
};
下面更加强化这种封装性
class Fred {
public:
static FredPtr create(); // Defined below class FredPtr {...};
static FredPtr create(int i, int j); // Defined below class FredPtr {...};
// ...
private:
Fred();
Fred(int i, int j);
// ...
};
class FredPtr { /* ... */ };
inline FredPtr Fred::create() { return new Fred(); //隐式构建FredPtr对象}
inline FredPtr Fred::create(int i, int j) { return new Fred(i,j); }
通过引用计数实现,拷贝写
class Fred {
public:
Fred(); // A default constructor
Fred(int i, int j); // A normal constructor
Fred(const Fred& f);
Fred& operator= (const Fred& f);
~Fred();
void sampleInspectorMethod() const; // No changes to this object
void sampleMutatorMethod(); // Change this object
// ...
private:
class Data {
public:
Data();
Data(int i, int j);
Data(const Data& d);
// Since only Fred can access a Fred::Data object,
// you can make Fred::Data's data public if you want.
// But if that makes you uncomfortable, make the data private
// and make Fred a friend class via friend class Fred;
// ...your data members are declared here...
unsigned count_;
// count_ is the number of Fred objects that point at this
// count_ must be initialized to 1 by all constructors
// (it starts as 1 since it is pointed to by the Fred object that created it)
};
Data* data_;
};
Fred::Data::Data() : count_(1) /*init other data*/ { }
Fred::Data::Data(int i, int j) : count_(1) /*init other data*/ { }
Fred::Data::Data(const Data& d) : count_(1) /*init other data*/ { }
Fred::Fred() : data_(new Data()) { }
Fred::Fred(int i, int j) : data_(new Data(i, j)) { }
Fred::Fred(const Fred& f)
: data_(f.data_)
{
++data_->count_;
}
Fred& Fred::operator= (const Fred& f)
{
// DO NOT CHANGE THE ORDER OF THESE STATEMENTS!
// (This order properly handles self-assignment)
// (This order also properly handles recursion, e.g., if a Fred::Data contains Freds)
Data* const old = data_;
data_ = f.data_;
++data_->count_;
if (--old->count_ == 0) delete old;
return *this;
}
Fred::~Fred()
{
if (--data_->count_ == 0) delete data_;
}
void Fred::sampleInspectorMethod() const
{
// This method promises ("const") not to change anything in *data_
// Other than that, any data access would simply use "data_->..."
}
void Fred::sampleMutatorMethod()
{
// This method might need to change things in *data_
// Thus it first checks if this is the only pointer to *data_
if (data_->count_ > 1) {
Data* d = new Data(*data_); // Invoke Fred::Data's copy ctor
--data_->count_;
data_ = d;
}
assert(data_->count_ == 1);
// Now the method proceeds to access "data_->..." as normal
}
在类层次中使用拷贝写
class Fred {
public:
static Fred create1(const std::string& s, int i);
static Fred create2(float x, float y);
Fred(const Fred& f);
Fred& operator= (const Fred& f);
~Fred();
void sampleInspectorMethod() const; // 不修改
void sampleMutatorMethod(); // 修改
// ...
private:
class Data {
public:
Data() : count_(1) { }
Data(const Data& d) : count_(1) { } // 不拷贝count_
Data& operator= (const Data&) { return *this; } // 不拷贝count_
virtual ~Data() { assert(count_ == 0); } // 虚析构函数
virtual Data* clone() const = 0; // 虚构造函数
virtual void sampleInspectorMethod() const = 0; // 纯虚函数
virtual void sampleMutatorMethod() = 0;
private:
unsigned count_; // count_ doesn't need to be protected
friend class Fred; // Allow Fred to access count_
};
class Der1 : public Data {
public:
Der1(const std::string& s, int i);
virtual void sampleInspectorMethod() const;
virtual void sampleMutatorMethod();
virtual Data* clone() const;
// ...
};
class Der2 : public Data {
public:
Der2(float x, float y);
virtual void sampleInspectorMethod() const;
virtual void sampleMutatorMethod();
virtual Data* clone() const;
// ...
};
Fred(Data* data);
// Creates a Fred smart-reference that owns *data
// It is private to force users to use a createXXX() method
// Requirement: data must not be NULL
Data* data_; // Invariant: data_ is never NULL
};
Fred::Fred(Data* data) : data_(data) { assert(data != nullptr); }
Fred Fred::create1(const std::string& s, int i) { return Fred(new Der1(s, i)); }
Fred Fred::create2(float x, float y) { return Fred(new Der2(x, y)); }
Fred::Data* Fred::Der1::clone() const { return new Der1(*this); }
Fred::Data* Fred::Der2::clone() const { return new Der2(*this); }
Fred::Fred(const Fred& f)
: data_(f.data_)
{
++data_->count_;
}
Fred& Fred::operator= (const Fred& f)
{
// DO NOT CHANGE THE ORDER OF THESE STATEMENTS!
// (This order properly handles self-assignment)
// (This order also properly handles recursion, e.g., if a Fred::Data contains Freds)
Data* const old = data_;
data_ = f.data_;
++data_->count_;
if (--old->count_ == 0) delete old;
return *this;
}
Fred::~Fred()
{
if (--data_->count_ == 0) delete data_;
}
void Fred::sampleInspectorMethod() const
{
// This method promises ("const") not to change anything in *data_
// Therefore we simply "pass the method through" to *data_:
data_->sampleInspectorMethod();
}
void Fred::sampleMutatorMethod()
{
// 拷贝写
if (data_->count_ > 1) {
Data* d = data_->clone(); // 虚构造,让子类决定如何拷贝自己
--data_->count_;
data_ = d;
}
assert(data_->count_ == 1);
// Now we "pass the method through" to *data_:
data_->sampleMutatorMethod();
}
是否可以绝对阻止人们破坏引用计数机制
有两个暗中破坏引用计数机制的途径
- 有人通过Fred* 而不是FrePtr 进行操作。如果FredPtr 类有一个operator*() 方法,返回Fred&。
FredPtr p = Fred::create();
Fred* p2 = &*p;
避免方法,重载Fred::operator&() 使其返回一个FredPtr,或修改FredPtr::operator*() 的返回类型,让其返回FredRef,它需要有Fred 的所有行为,并将调用转发给真正的Fred;或者,直接砍掉FredPtr::operator*(),这也失去了使用Fred& 的能力。但即使这样,客户仍然可能通过operator->得到Fred*:
FredPtr p = Fred::create();
Fred* p2 = p.operator->();
- Fred 可能是安全的,此时,FredPtr可能泄露和/或 悬挂,FredPtr 可能不安全。2.1 通过new 创建FredPtr可能造成内存泄露,避免方法,将FredPtr::operator new()设置为私有2.2 创建FredPtr本地对象后,通过& 符号得到FredPtr指针,之后,非法引用,方法:FredPtr::operator&() 私有。2.3 FredPtr p;FredPtr &q = p;//此时又泄露了
即使我们补上了所有的漏洞,C++ 有一系列的语法,称为指针转换,无法避免的导致问题。
经验:
1… 无论如何努力,无法躲过间谍
2… 可以尽量的避免错误的出现
推荐:使用易于构建且易于使用的机制来防止错误,并且不要试图阻止间谍活动。
因此,如果我们不能使用C++ 语言来阻止间谍活动,还有其它方法吗?是,使用code review。间谍技术通常涉及一些奇怪的语法和/或使用指针转换和联合,可以用工具来指出大多数“热点”。
c++ 如何使用垃圾回收
C++ Programming Laguage和site for c and c++ garbage collection 中有相关的介绍。http://www.stroustrup.com/4th.html
http://www.hpl.hp.com/personal/Hans_Boehm/gc
垃圾回收:http://www.iecc.com/gclist/GC-faq.html