小言_互联网的博客

C++ 内存模型

330人阅读  评论(0)

C++ 内存模型(未完。。。)

数据存储

  • 程序数据段
  • 程序代码段

stack栈内存

  • 栈内存属于执行期函数,编译时确定大小
  • 函数执行时栈空间自动分配,结束时栈空间自动销毁
  • 栈对象是线性分配,连续排列,没有内存碎片化效应
  • 栈内存具有函数本地性,不存再多线程竞争
  • 栈内存有大小限制,可能存在益处,linux默认未8MB,windows默认是1MB
  • 栈内存使用对象或引用直接使用,管理复杂度较低

heap堆内存

  • 堆内存具有全局共享特点,大小动态变化
  • 堆对象分为时手动分配(malloc/new),对象释放时,手动释放内存(delete/free)
  • 堆生对象链式分配,非连续排列
  • 堆内存全局共享,存在多线程竞争的可能性
  • 堆内存的大小没有栈内存严格限制,与机器内存总量和进程寻址空间有关
  • 对内存使用指针简介访问,管理复杂度较高

堆与栈

  • 栈内存分配快,内存连续,缓存友好,释放快
  • 生存中期短,拷贝少(传值、返回值),栈内存性能比堆内存性能好
  • 堆内存有更好的灵活性,但性能比栈内存差
  • 堆内存再长期运行的程序中有内存碎片化的效应,会造成小快内存无法使用(内存泄漏),降低缓存使用效率
  • 堆内存分配需要寻找合适内存大小的内存块,花费时间更多
  • 栈对像可以使用RAII和指针以动操作避免拷贝的代价

值语义和引用语义

  • 值语义:对象以值的方式直接存储,传值,返回值,拷贝等
  • 引用语义:对象已引用或指针的方式间接存储,穿传值,返回值,拷贝传递的是引用或指针
  • 值语义没有悬浮指针/引用,没有昂贵的释放操作,没有内存泄漏,没有数据竞争,但是值语义堆大对象拷贝代价高,不能支持虚函数多态

变量初始化

  • 使用统一初始化:int a{50}; int a2 = {50};
  • 赋值初始化:int a3 = 50;
  • 构造初始化:int a4(50);
  • 推荐使用统一初始化(列表初始化),可以避免隐式转换

指针容易出现的问题

  • 内存泄漏——忘记释放已经申请的内存
  • 悬浮指针——使用已经释放的内存,返回栈对象地址
  • 重复删除——对已经删除的对象进行二次删除
  • 删除非堆对象指针——对栈对像、全局/静态对象地址进行删除
  • 使用空指针/使用失效的引用
int* glob;
void process(int* q){
   
  glob = q; 
}
void g1(){
   
  int* p = new int{
   7};
  process(p);
  delete p;
  *glob = 9; //悬浮指针
}
void g2(){
   
  int* p = new int{
   7};
  process(p);
  delete p; 
  delete glob; //重复释放
}
void g3(){
   
  int x = 7;
  int* p = &x;
  process(p);
  delete glob;// 释放栈指针
}

 

对象内存空间

  • 虚函数占用一个指针的大小
  • 静态数据成员不参与类堆对象大小的计算
  • 初始化顺序按照成员变量声明顺序
  • 可以使用#pragma pack(4)来控制对象内存对齐的大小

特殊成员函数与三法则

  • 三法则:析构函数、拷贝构造函数、赋值操作符,三者自定义其一,则需要同时定义另外两个(否则编译器自动生成的一般语义有误)
  • 使用default让编译器自动生成;使用delete让编译器不要自动生成;
  • 编译器自动生成的的拷贝/赋值时按字节拷贝,若出现指针等具有动态数据成员的对象,通常需要自己定义拷贝/赋值/构造行为
  • 赋值操作符中的copy和swap是较为好的方法(注意赋值操作中需要避免自我赋值的情况)
  • 普通类的成员函数其实相当远一个带有this指针的全局函数。静态成员函数其实不属于类,是一个全局函数,没有this指针;

性能指南

  • 如果函数非常小,且时间很敏感,可已将其声明未inline
  • 如果函数可能在编译器进行求值,将其声明为constexpr
  • 尽量使用类初始化列表对类成员变量进行初始化
  • explicit 避免隐式转换
  • 使用重载避免引述转换
  • 返回值优化(RVO)

vector 与 array

  • vector在栈上,array在堆上
  • vector使用时,建议设置大小,reserve(1000),避免vector的内存增长带来的拷贝
class MyClass {
   
public:
	MyClass() = default;//强制生成默认的构造函数
	~MyClass() = delete;//强制删除改构造函数
	MyClass(const MyClass& tmp) = delete;
	MyClass(const MyClass&& tmp) = delete;//移动拷贝
	MyClass& operator=(const MyClass& tmp) = delete;
	MyClass& operator=(const MyClass&& tmp) = delete;//移动赋值
};
class Point
{
   
public:
	Point(int x, int y) : x_(x), y_(y) {
   }
	int x_;
	int y_;
};
class Rectangle
{
   
public:
	Point* left_up_;
	int length_;
	int width_;
	//禁止构造函数隐式转换
	explicit Rectangle(int date) {
   }
	Rectangle(int x, int y, int length, int width) : left_up_(new Point(x,y)), length_(length), width_(width) {
   }
	Rectangle(const Rectangle& rect) : length_(rect.length_), width_(rect.width_)
	{
   
		if (rect.left_up_ != nullptr)
			left_up_ = new Point(*rect.left_up_);
		else
			left_up_ = nullptr;
	}

	Rectangle& operator=(const Rectangle& rect)
	{
   
		if (this == &rect)
			return *this;
		Rectangle temp(rect);
		swap(left_up_, temp.left_up_);
		this->length_ = temp.length_;
		this->width_ = temp.width_;

		return *this;
	}

	~Rectangle()
	{
   
		delete left_up_;
		left_up_ = nullptr;
	}
	
	inline friend ostream& operator<<(ostream& os, const Rectangle& rect);
};

ostream& operator<<(ostream& os, const Rectangle& rect)
{
   
	return os << "{" << rect.left_up_->x_ << "," << rect.left_up_->y_ << "," << rect.length_ << "," << rect.width_ << "}" << endl;
}

 

转载:https://blog.csdn.net/xiaofeilongyu/article/details/128353295
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场