您的位置:首页 > 编程语言 > C语言/C++

剑指offer——基础知识之C++

2017-06-09 15:43 597 查看

1. C++(封装,继承和多态)

考点一:sizeof

sizeof(object) or sizeof(typename)//结果为字节

数据类型的sizeof

/*c++基本数据类型*/
//32位
sizeof(char)==1;
sizeof(short int)==2;
sizeof(int)==4;
sizeof(long int)==4;
sizeof(float)==4;
sizeof(double)==8;
sizeof(long double)==10;
sizeof(unsigned int)==sizeof(int);//unsigned不能影响sizeof的取值
/*函数类型*/
sizeof(函数名(参数列表))==sizeof(函数返回类型);
sizeof(void);//错误的,无法对void类型使用sizeof
sizeof(函数名);//错误的,无法对函数指针使用sizeof
//注意:不管是什么类型的指针,大小都是4的,因为指针就是32位的物理地址。
//数组:数组的大小是各维数的乘积*数组元素的大小,动态分配的数组是指针。


结构的sizeof

复合数据类型,如union,struct,class的对齐方式为成员中对齐方式最大的成员的对齐方式。

结构体成员对齐的规则:(1)结构体内的成员的首地址相对于结构体首地址的偏移量是其类型大小的整数倍,比如说double型成员相对于结构体的首地址的偏移量应该是8的整数倍;(2)结构体大小等于结构体内最大成员大小的整数倍;(3)为了满足规则1和规则2编译器会再结构体成员之后进行字节填充。

struct MyStruct {
char c; //偏移量1的倍数,0满足,占用一个字节
double d; //偏移量8的倍数,8满足填充7个字节,占8个字节
int i; //偏移量必须为sizeof(int)即4的倍数,16满足,占4个字节
};//大小为1+7+8+4=20不是8的倍数,所以结构大小为24.


/#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。

#pragma pack(push) //保存对齐状态
#pragma pack(4)//设定为4字节对齐
struct test {
char m1; //偏移量为0,满足我们自己设定的4字节对齐),占1个字节
double m4; //偏移量为1,需要补足3个字节,这样使偏移量满足为n=4的倍数(因为sizeof(double)大于n),m4占用8个字节
int m3; //这时其偏移量为12,满足为4的倍数,m3占用4个字节
}; //所有成员变量分配了空间,共分配了16个字节,满足为n的倍数。
#pragma pack(pop)//恢复对齐状态


注意:结构或者类中的静态成员不对结构或者类的大小产生影响,因为静态变量的存储位置与结构或者类的实例地址无关。

类的sizeof

定义一个空的类型(无任何成员变量和成员函数),对该类型求sizeof结果为1.

解释:空类型的实例中不包含任何信息,本来就sizeof应为0,但当我们声明该类型的实例时,它必须在内存中占有一定的空间,否则无法使用这些实例(由编译器觉得具体占用多少内训,Visual Studio中每个空类型的实例占1字节的空间)。

若向上述空类型添加一个构造函数和析构函数,结果仍然为1.

解释:调用函数只需知道函数地址即可,这些函数的地址只与类型有关,与类型实例无关,所以编译器不会再实例内存中添加任何额外信息。

若析构函数为虚函数,结果为4(32位系统)或8(64为系统).

解释:通常编译器处理虚函数的方法是:给每一个对象添加一个隐藏成员,其保存了一个指向虚函数表的指针。

考点二:类的成员函数(构造,析构,复制,赋值…)

复制构造函数

不允许复制构造函数按值传参,会导致编译出错。

class test{
private:
...
public:
...
test(const test& t);//复制构造参数,按值传参:test(test t);
...
}


赋值运算符函数

class test{
private:
char* pData;
public:
...
test& operator=(const test& t);//返回类引用(允许连续赋值),参数为常量引用
...
}
//教材参考代码
test& test::operator=(const test& t){
if(this == &t) return *this;
delete[] pData;
pData = NULL;
pData = new char[strlen(t.pData)+1];
strcpy(pData,t.pData);
return *this;
}
//考虑异常安全性
test& test::operator=(const test &t){
if (this != t){
test temp(t);//创建临时实例
char* pTemp = temp.pData;
temp.pData = pData;
pData = pTemp;
}
return *this;
}


考点三:常量指针和指针常量

//*(指针)和 const(常量) 谁在前先读谁
int const *p;//const在前,所以p为常量指针
int* const p;//*在前,所以p为指针常量
/*
*象征着地址,const象征着内容;谁在前面谁就不允许改变。
常量指针:const在前,所以p的内容不能变,指向的地址可以变;
指针常量:*在前,所以p指向的的地址不能变,内容可以变;
*/


考点四:函数传参,函数模板,模板函数

函数传参方式

按值传参

利用值传递方式,实际上是把实参的内容复制到形参中,实参和形参是存放在两个不同的内存空间中。在函数体内对形参的一切修改对实参都没有影响;如果形参是类的对象,利用值传递的话每次都要调用类的构造函数构造对象,效率比较低。

指针传参

当进行指针传递的时候,形参是指针变量,实参是一个变量的地址或者是指针变量,调用函数的时候,形参指向实参的地址;指针传递中,函数体内可以通过形参指针改变实参地址空间的内容。

引用传参

1.引用实际上是某一个变量的别名,和这个变量具有相同的内存空间。

实参把变量传递给形参引用,相当于形参是实参变量的别名,对形参的修改都是直接修改实参。

在类的成员函数中经常用到类的引用对象作为形参,大大的提高代码的效率

函数模板

template <class T>
<返回类型> 函数名(参数列表){}
//参数列表可以含有具体类型
//局限性,编写的函数模板无法处理某些类型,比如
template <typename T>
void f(T a,T b){
...
a = b;//复制操作:当T为数组类型时,不可行
if (a>b) {...}//比较操作:当T为数组类型时,不可行
T c = a*b;//乘法运算符:当T为数组,指针或结构时,不可行
...
}


函数模板的数据类型参数标识符实际上是一个类型形参,在使用函数模板时,要将这个形参实例化为确定的数据类型。将类型形参实例化的参数称为模板实参,用模板实参实例化的函数称为模板函数
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: