从零开始学C++之c++类和对象<中>
6.结构体内存对齐规则:
- 第一个成员在与结构体偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8- 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是
所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
5.可以通过#pragma pack?=(num)来修改默认对齐数。
举个栗子:有如下代码计算下面结构体的大小:
using namespace std;
struct A
{
//vs的默认对齐数是8
int a;//4字节
int b;//4
double d;//8
int e;//4-20 默认对齐数的整数倍 24
};
struct B
{
char a;//1
double d;//8+8=16
int c;//16+4=20
char e;//21-24
};
struct B
{
char a;//1
double d;//8+8=16
char e;//17
A objA;//24+24=48
};
内存对齐中的一些题目:
- 为什么要进行内存对齐?
主要目的是为了提高效率,获取数据时,一般是按照4个字节或者8个字节的代码块进行获取,进行内存对齐时,不用进行内容的切割和拼接,提高读取数据的效率 - 如何让结构体按照指定的对齐参数进行对齐?
可以通过#pragma pack?=(num)来修改默认对齐数。 - 如何知道结构体中某个成员相对于结构体起始位置的偏移量?
使用地址相减
A a;
cout << (char*)&a.d - (char*)&a << endl;
- 什么是大小端?如何测试某台机器是大端还是小端,有没有遇到过要考虑大小端的场景
大端:低地址存高位,高地址存低位;
小端:高地址存低位,低地址存高位;
测试大小端的方法:
using namespace std;
union A
{
int a;
char b;
};
void test1()
{
A obja;
obja.a = 1;
if (obja.b)
cout << "小端" << endl;
else
cout << "大端" << endl;
}
void test2()
{
int a = 1;
char* pa = (char*)&a;
if (*pa)
cout << "小端" << endl;
else
cout << "大端" << endl;
}
7.this指针
1.每一个非静态成员函数中,都有一个this指针;
2.作为函数的第一个形参;
3.this指向当前调用此函数的对象
4.this类型:类类型*const this 一般不会变化
5.只存在成员函数中
6. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this
形参。所以对象中不存储this指针。
7. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
#include<iostream>
using namespace std;
class Date
{
public:
//每一个非静态成员函数中,都有一个this指针;
//作为函数的第一个形参;
//this指向当前调用此函数的对象
//this类型:类类型*const this 一般不会变化
void Display()//void Display(this)
{
cout << _year << "-" << _month << "-" << _day << endl;
}
void SetDate(int year, int month, int day)
{//void SetDate(this, int year, int month, int day)
this->_year = year;
_month = month;
_day = day;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
int main()
{
Date d1, d2;
d1.SetDate(2018, 5, 1);
d2.SetDate(2018, 7, 1);
d1.Display();
d2.Display();
return 0;
}
8.this指针可以为空,但是不能进行解引用
8.类的6个默认成员函数
如果一个类中什么成员都没有,简称为空类。任何一个类在我们不写的情况下,都会自动生成下面6个默认成员函数。
类的6个默认的成员函数包括:构造函数、析构函数、拷贝构造函数、赋值运算符重载函数、取地址操作符重载、const修饰的取地址操作符重载。
9.构造函数
9.1构造函数的概念
构造函数是一个特殊的成员函数,名字与类名相同**,创建类类型对象时由编译器自动调用**,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次。
9.2 特性
构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
其特征如下:
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。
- 构造函数可以重载。构造可以重载,无参的话默认构造。
- 编译器自动生成的构造也是无参的构造函数 全缺省也是默认构造,没有显示定义任何一个构造函数,编译器会自动生成无参构造,否则不会自动生成。
- 任何一个构造函数(包括编译器自动生成的)都会自动调用自定义成员的默认构造,如果没有默认构造,编译器会报错
举个栗子:
class Date
{
public:
// 1.无参构造函数
Date()
{}
//全缺省:也是一种默认构造
Date(int year=1, int month=1, int day=1)
{
_year = year;
_month = month;
_day = day;
}
// 2.带参构造函数
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
void TestDate()
{
Date d1; // 调用无参构造函数
Date d2(2015, 1, 1); // 调用带参的构造函数
//不能显示调用构造函数
//d.Data(2020,2,2)
// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
//不是调用了无参构造,而是声明一个函数
//函数名:d3
//参数列表:空
//返回值:Data
Date d3();
}
9.3总结:
1.构造函数是初始化对象的内容,不是创建对象
2.创建对象时,编译器自动调用构造函数,不能显式调用
3.如果没有显式定义构造,则编译器自动生成无参构造,否则编译器不再生成无参构造。
4.默认构造只能有一个,无参构造和全缺省都是默认构造;
5.如果有自定义成员,则构造函数会自动调用自定义成员的默认构造。
10.析构函数
10.1概念
析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。不是销毁对象或者变量。
10.2特征
- 析构函数名是在类名前加上字符 ~。
- 无参数无返回值。
- 一个类有且只有一个析构函数。
- 对象销毁的时候,编译器自动调用析构,用来进行资源的清理。
- 不显式定义析构,编译器会自动生成默认的析构函数
- 析构函数自动调用自定义成员的析构函数,完成自定义成员的资源清理
typedef int DataType;
class SeqList
{
public :
SeqList (int capacity = 10)
{
_pData = (DataType*)malloc(capacity * sizeof(DataType));
assert(_pData);
_size = 0;
_capacity = capacity;
}
~SeqList()
{
if (_pData)
{
free(_pData ); // 释放堆上的空间
_pData = NULL; // 将指针置为空
_capacity = 0;
_size = 0;
}
}
private :
int* _pData ;
size_t _size;
size_t _capacity;
};
11.拷贝构造函数
11.1概念
构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
例如:
Date d(2020, 2, 3);
//调用拷贝构造,创建copy对象,内容和d完全相同
Date copy(d);
调用拷贝构造,创建copy对象,内容和d完全相同
11.2 特征
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。
- 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝
- 有资源的类,必须显式定义拷贝构造