文章目录
类的大小及空类
- 一个类的大小,实际就是该类中”成员变量”之和,当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。
构造拷贝构造的优化
- 当有临时对象产生时,构造拷贝构造连续发生,编译器会直接优化中间步骤1-》3
初始化列表、匿名对象、类型的销毁顺序
- 匿名对象,生命周期旨在这一行
A()
析构函数顺序分析
- 栈帧压栈后进先出
- 全局和静态的区别
- 全局在main函数之前就初始化了
- 静态是在 第一次调用域函数的时候 初始化
- 先释放静态,再释放全局
初始化列表初始化,提高效率,可认为是成员变量初始化的地方,强制的有:引用 强制,自定义类型成员
- 双向带头循环的例子证明声明的次序有关:
ListNode(int x)
:val(x)
, next(nullptr)
{
cout << "ListNode()" << endl;
}
隐式类型转换、静态关键字详解
隐式类型转换
- 内置类型转换成自定义类型,先用内置类型创建一个匿名对象,再用匿名对象拷贝构造给另一个对象,中间优化了。
- C++11支持多个参数,explicit关键字可以阻止。
public:
Date(int year)
:_year(year)
{}
explicit Date(int year)
:_year(year)
{}
Date d1(2018);
d1 = 2019;/date(2019)->拷贝给临时变量,临时变量拷贝给形参
const int *p1
; const修饰的是int,表示p1指向的变量值不可改变,指针本身可以改变.
静态static
- 私有的静态变量,受到类域和访问限定符,更好体现封装,别人不能轻易修改
- 提供一个共有的成员函数,利用匿名对象去调用;公有访问,
类名::对象
。 - 修饰全局变量或函数,改变的是链接接属性,旨在当前文件可见
- 定义全局变量,明确调用了多少个构造对象
输入输出操作符及重载、友元函数及类
友元函数
- 放在类里面的任何地方
- 还可以定义友元类,单向的,A是B的友元类,B不能访问A,A可以B
cin、cout重载
- 为了让cout在第一个参数,左左操作数,我们就只能写成全局的
- 其次就是
operator<<
搞成友元,可以在类中访问私有。 - 但是
operator<<
不是必须友元,还有其他方式,所以能不使用就不使用,因为破坏了封装。
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "/" << d._month << "/" << d._day << endl;
return out;
}
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
内部类
- 在类的里面定义另外一个类,计算大小不考虑内部类
- 受到A的类域的限制和访问限定符的限制,天生友元
- 还可以访问外类的静态
New、Delete、内存布局
New
是对operator new
和构造函数的封装,是向系统申请。new[]
申请的是连续空间,同时会执行N次构造函数,new在申请空间失败时会抛异常。new()
是初始化
try
{
char* p2 = new char[0x7fffffff]; / 出错,抛异常,调到捕获异常的位置
}
catch (const exception& e)
{
cout << e.what() << endl;
}
delete
- delete是对
operator delete
和析构函数的封装,delete[] 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理,下od同理。 operator new/ operator delete
是两个全局函数,可实现向内存池申请和释放,提高效率。ONOD可以实现重载专属,可以记录一些信息。
placement-new
- placement-new :在已分配的原始内存空间中调用构造函数初始化一个对象
- 使用格式:
new (place_address) type
或者new (place_address) type(initializer-list)
- place_address必须是一个指针,initializer-list是类型的初始化列表
- 定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,
- 所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
- 使用格式:
class Test
{
public:
Test()
: _data(0)
{
cout<<"Test():"<<this<<endl;
}
~Test()
{
cout<<"~Test():"<<this<<endl;
}
private:
int _data;
};
// pt现在指向的只不过是与Test对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执
Test* pt = (Test*)malloc(sizeof(Test));
// 如果Test类的构造函数有参数时,此处需要传参
new(pt) Test
在堆上申请4G的内存
//建议改为x64位
#include <iostream>
using namespace std;
int main()
{
void* p = new char[0xfffffffful];
cout << "new:" << p << endl;
return 0;
}
内存泄漏的危害和防止
- 检测工具和智能指针
- 危害:可能会被开除…
阔怕啊