C++笔记

1. i++和++i哪个效率更高

  • 从内建数据类型来看,去除编译器优化的影响,两者都只是简单地用于增加一元操作数,没有任何区别。
  • 考虑自定义数据类型(类等),++i可以返回对象的引用;i++必须返回对象的值(必须产生一个临时对象保存更改前对象的值并返回),导致在大对象的时候产生了较大的复制开销,引起效率降低。

2. 两个变量值的交换方法

  • 中间变量
  • 加减运算,可能导致溢出
void swap(int &a, int &b)
{
        a = a + b;
        b = a - b;
        a = a - b;
}
  • 异或运算
void swap(int &a, int &b)
{
        a ^= b;
        b ^= a;
        a ^= b;
}

3. 头文件引用中<>与""的区别

  • <>表表明括号中的文件是一个工程或标准头文件,会首先查找系统目录,找不到再找当前目录。
  • ""表示是用户提供的头文件,会从当前文件目录或文件名指定的目录寻找,找不到再查找系统目录。

4. #define与const的区别

  • define只是简单的文本替换,预处理的时候编译器会做文本替换,因此#define常量的生命周期止于预处理期,它存在于程序的代码段,在实际程序中只是一个常数,一个命令中的参数,没有实际的存在。
  • const存在于程序的数据段,于堆栈中分配了空间,它在程序中确确实实的存在着,并可以被调用、传递。const常量有数据类型,可以进行类型安全检查。

5. const的作用

  • 定义常量。
  • 修饰函数形式参数。函数输入参数为自定义类型时,传引用比传值效率更高(临时对象)。但只用引用有可能改变原值,所以加const。
  • 修饰函数的返回值。如果给“指针传递”的函数返回值加const,则返回值不能被直接修改,且只能被赋值给const修饰的同类型指针。
  • 修饰类的成员函数。不需要修改数据成员的函数都应该用const修饰,这样不小心修改了数据成员或调用了非const成员函数,编译器会报错。

6. static的作用

  • 在函数体,一个被声明为静态的变量在函数被调用的过程中维持其值不变。
  • 模块内函数体外,被声明为静态的变量和函数可以被模块内所有函数访问,但不能被模块外访问。
  • 类中的static数据成员,属于类所有,所有的对象使用同一份数据。
  • 类中的static成员函数,不接受this指针,只能访问static数据成员。

7. sizeof和strlen的区别

  • sizeof是操作符,strlen是函数。
  • strlen只能用char*做参数,且必须"\0"结尾,sizeof可以计算任意类型。
  • sizeof计算的是类型占内存的大小,strlen用来计算字符串的长度。

8. C++为什么引入内联函数

  • 替代宏定义。宏定义的缺点:只是简单的文本替换,无法进行有效性检测和类型安全检查,另外它的返回值也不能被强制转换为可转换的合适类型。内联函数是一个真正的函数,可以进行一系列的相关检查,保证调用正确。
  • 内联函数代码被放入符号表(我们把函数和变量统称为符号,每个目标文件都有一个符号表,记录了目标文件中用到的所有符号)中,使用时直接替换,没有调用的开销,效率很高。
  • 作为某个类的成员函数,使用类的保护成员和私有成员。(在类内定义的成员函数默认为inline,当然,是否内联还是取决于编译器。宏定义做不到,因为无法将this指针放在合适的位置)

9. 什么情况下不宜使用内联函数

  • 函数代码比较长。会导致内存消耗代价较高。
  • 函数内出现循环。执行函数的时间要比函数调用的开销大。
  • 构造和析构函数。可能会执行基类的构造析构函数,无限套娃。

10. 指针和引用的区别

  • 初始化要求不同。引用创建时必须初始化,而指针不必初始化。
  • 可修改性不同。引用一旦被初始化,就不能被另一个对象引用;而指针在任何时候都可以指向另一个对象。
  • 不存在NULL引用,而指针可以是NULL。

11. 复杂指针的声明

  • 定义一个整数:int a;
  • 定义一个指向整数的指针:int *a;
  • 定义一个指向指针的指针,它指向的指针指向一个整数:int **a;
  • 定义一个有十个整数的数组:int a[10];
  • 定义一个有十个指针的数组,指针指向整数:int *a[10]
  • 定义一个指向有十个整数数组的指针:int (*a)[10]
  • 定义一个指向函数的指针,该函数有一个整型参数并返回一个整数:int (*a)(int)
  • 定义一个有十个指针的数组,指针指向函数, 该函数有一个整型参数并返回一个整数:int(*a[10])(int)

12. 指针运算

指针的运算就是地址的运算,因此指针运算不同于普通变量,它只允许有限的几种运算:

  • 指针指向某一存储单元,静态存储区、栈、堆上有所不同:
    https://blog.csdn.net/qq_29762941/article/details/80738129
  • 指针与整数相加或相减,用来移动指针:对指针进行+1操作,得到的是下一个元素的地址,而不是原地址+1,所以类型为t的指针的移动是以sizeof(t)为移动单位。
int a[5] = {1,2,3,4,5};
//a与&a的地址是一样的,但含义不一样,a是数组首地址,也就是a[0]的地址,&a是数组的地址。
//a+1是数组下一元素的地址,即a[1],而&a+1是下一个数组的地址,即a[5],这时候已越界,一般需要强制类型转换。
  • 指针比较,得到存储位置的先后。
  • 指针间相加减,得到两个地址之间的数据个数。

13. 指针常量和常量指针

  • 常量指针:指向常量的指针,本身可改,但不能改所指向的常量。
  • 指针常量:指针是常量,本身不可改,但可改指向的对象。
#include<iostream>
using namespace std;
int main()
{
    char *str[] = { "welcome", "to", "fortemedia", "nanjing" };
    char **p = str + 1;
    str[0] = (*p++) + 2;
    str[1] = *(p+1);
    str[2] = p[1] + 3;
    str[3] = p[0] + (str[2]-str[1]);

    cout << str[0] << endl;
    cout << str[1] << endl;
    cout << str[2] << endl;
    cout << str[3] << endl;

    return 0;
};

image.png

14. this指针

C++中,对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。this指针的作用域是类内部,在类的非静态成员函数中访问类的非静态成员时,编译器会自动将对象地址作为一个隐含参数传递给函数。

15. typedef

在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。

16. malloc/free和new/delete的区别

  • malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。
    运算符是语言自身的特性,它有固定的语义,而且编译器也知道意味着什么。就像 +-* 一样,由编译器解释语义,生成相应的代码;库函数是依赖于库的,没有库就没有它,也就是一定程度上独立于语言的。理论上,编译器不知道也不关心函数的作用,编译器只保证编译函数,以及调用该函数时参数和返回值符合语法,并生成相应 call 函数的代码。但实际中一些高级点的编译器,都会对标准库自带的一些函数进行特别处理。
  • 对于非内部数据类型的对象而言,仅用maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,因此不能把执行构造函数和析构函数的任务强加于malloc/free函数。
  • new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。
    自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配。
    自由存储区是否能够是堆,取决于operator new 的实现细节。
  • new返回的是对象类型的指针,而malloc内存分配成功则是返回void *。
  • new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL。
  • C++提供了new[]与delete[]来专门处理数组类型,malloc需要手动自定数组的大小。

参考https://www.cnblogs.com/QG-whz/p/5140930.html

17. 各种标准内存分配函数的使用

  • malloc 调用形式为(类型*)malloc(size)。在内存的动态存储区中分配长度为“size”字节的连续区域,返回该区域的首地址,此时内存中的值没有初始化,是一个随机数。
  • calloc调用形式为(类型*)calloc(n,size)。在内存的动态存储区中分配n块长度为“size”字节的连续区域,返回首地址,此时内存中的值都被初始化为0。
  • realloc调用形式为(类型)realloc(ptr,size)。将ptr内存大小增大到“size”,新增加的内存块没有初始化。
  • free的调用形式为free(void*ptr):释放ptr所指向的内存空间。
  • C++中用new和delete函数,调用类的构造函数和析构函数。

18. 使用库函数将数字转换为字符串

  • itoa():将整型值转换为字符串。
itoa(num_int, str_int, 10)  //把整数num_int转换为字符串str_int,10表示十进制
  • ltoa():将长整型值转换为字符串。
  • ultoa():将无符号长整型值转换为字符串。
  • gcvt():将浮点型数转换为字符串,取四舍五入。
gcvt(num_double, 8, str_double)  //把浮点数num_double转换为字符串str_double,8表示精确位数
  • ecvt(double value, int ndigit, int *decpt, int *sign)
    将双精度浮点型值转换为字符串,转换结果中不包含十进制小数点。
    value:待转换的双精度浮点数。
    ndigit:存储的有效数字位数。如果value中的数字个数超过ndigit,低位数字被舍入。如果少于ndigit个数字,该字符串用0填充。
    *decpt:存储的小数点位置。0或负数指小数点在第一个数字的左边。
    *sign:转换的数的符号。如果该整数为0,这个数为正数,否则为负数。
  • fcvt():ndigit是小数点后面的位数,其余同ecvt()。

19. 使用库函数将字符串转换为数字

  • atof():将字符串转换为双精度浮点型值。
  • atoi():将字符串转换为整型值。
  • atol():将字符串转换为长整型值。
  • strtod():将字符串转换为双精度浮点型值,并报告不能被转换的所有剩余数字。
  • strtol():将字符串转换为长整值,并报告不能被转换的所有剩余数字。
  • strtoul():将字符串转换为无符号长整型值,并报告不能被转换的所有剩余数字。

20. 关键字volatile有什么含义

定义为volatile的变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值。准确地说,优化器在用到volatile变量时必须小心地重新读取该变量的值,而不是使用保存在寄存器里的备份。

21. 临时对象

临时对象不是局部变量,它是不可见的,会执行构造和析构函数,对程序性能可能产生影响。三种常见的创建情况:

  • 以值的方式给函数传参
  • 类型转换
#include <iostream>
using namespace std;

class TempObj {
public:
    TempObj(int m = 0, int n = 0);
    TempObj(TempObj& to)
    {
        cout << "Copy Constructor" << endl;
        m_Number = to.m_Number;
        m_Size = to.m_Size;
    }
    ~TempObj()
    {
        //cout << "Deconstructor" << endl;
    }
    int GetSum(TempObj &ts);
public:
    int m_Number;
    int m_Size;
};

TempObj::TempObj(int m, int n)
{
    cout << "Default constructor" << endl;
    m_Number = m;
    m_Size = n;
    cout << "m_Number=" << m_Number << " " << "m_Size=" << m_Size << endl;
}

int TempObj::GetSum(TempObj &ts)
{
    int tmp = ts.m_Number + ts.m_Size;
    ts.m_Number = 1000;
    return tmp;
}

void main()
{
    TempObj tm(10, 20), sum;
    sum = 1000;
    cout << "sum=" << tm.GetSum(sum) << endl;
}
图片.png

main函数创建了两个对象,但输出却调用了三次构造函数。关键在sum = 1000;本身1000和sum类型不符,但编译器为了通过编译以1000为参数调用构造函数创建了一个临时对象。

  • 函数需要返回对象时

22. 判断处理器使用Big_endian还是Little_endian模式

联合体union的存放顺序是所有成员都从低地址开始存放,利用该特性,轻松地了解CPU对内存采用的读写模式。若处理器使用Big_endian 模式存储数据,则返回 0;若是用Little_endian模式存储数据,则返回1。

int checkCPU()
{
    union w
    {
        int a;
        char b;
    }c;
    c.a = 1;
    return(c.b == 1);
}

23. struct和class的区别

  • C的struct与C++的class的区别:struct只是作为一种复杂数据类型定义,不能包含成员函数,不能用于面向对象编程。
  • C++中的struct 和 class的区别:对于成员访问权限以及继承方式,class 中默认的是private,而struct中则是public。class还可以用于表示模板类型,struct则不行。

24. 类成员的初始化

class Obj
{
public:
    Obj(int k) :j(k),i(j)
    {
    }
private:
    int i;
    int j;
};

这里很容易让人误认为先对j进行初始化,然后再用j对i进行初始化。实际上,初始化列表的初始化顺序与变量声明的顺序一致,而不是按照出现在初始化列表中的顺序。这里成员i比成员j先声明,因此正确的顺序是先用j对i进行初始化,然后再对j进行初始化。由于在对i进行初始化时j尚未被初始化,j的值为随机值,故i的值也为随机值。

25. 初始化列表和赋值的区别

当类中含有const、reference成员变量以及基类的构造函数都需要初始化列表。

26. 静态成员变量和静态成员函数

  • 为了与非静态成员变量相区别,静态成员变量不能在类内部被初始化。可以把静态成员变量放在类定义外面初始化。
  • 静态成员函数和静态成员变量一样,不属于类的对象,因此不含this指针,也就无法调用类的非静态成员。

27. 空类默认情况下会产生哪些类成员函数?

  • 默认构造函数和复制构造函数,它们被用于类的对象的构造过程。
  • 析构函数,它被用于类的对象的析构过程。
  • 赋值函数,它被用于同类的对象间的赋值过程。
  • 取值运算,当对类的对象进行取地址(&)时,此函数被调用。

28. explicit构造函数

普通构造函数能够被隐式调用,而explicit构造函数只能被显示调用。

29. 构造函数的执行顺序与构造函数相反。

30. 什么是复制构造函数,以及使用它的场合。

复制构造函数是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构造及初始化。在C++中,3种对象需要复制,此时复制构造函数将会被调用:

  • 一个对象以值传递的方式传入函数体。
  • 一个对象以值传递的方式从函数返回。
  • 一个对象需要通过另外一个对象进行初始化。

31. 一组重载函数中,只能有一个函数被指定为extern "C"。

32. C++虚函数实现细节

简单地说,虚函数是通过虚函数表实现的。事实上,如果一个类中含有虚函数,则系统会为这个类分配一个指针成员指向一张虚函数表(vtbl),表中每一项指向一个虚函数的地址,实现上就是一个函数指针的数组。
虚函数表既有继承性又有多态性。每个派生类的虚函数表继承了它各个基类的虚函数表,如果基类虚函数表中包含某一项,则派生类的虚函数表中也将包含同样的一项,但是两项的值可能不同。如果派生类覆盖(override)了该项对应的虚函数,则派生类虚函数表的该项指向重载后的虚函数,在没有重载的情况下,则沿用基类的值。
在类对象的内存布局中,首先是该类的虚函数表指针,然后才是对象数据。在通过对象指针调用虚函数时,编译器生成的代码将先获取对象类的虚函数表指针,然后调用虚函数表中对应的项。对于通过对象指针调用的情况,在编译期间无法确定指针指向,但是在运行期间执行到调用语句时,这一点已经确定,编译后的调用代码能够根据具体对象获取正确的虚函数表,调用正确的虚函数,从而实现多态性。

33. 在构造函数中,虚拟机制不会发生作用。因为基类的构造函数在派生类的构造函数之前执行,基类构造函数运行时,派生类的成员还没有初始化,向下匹配是危险的。

34. 多重继承的缺点

  • 同名二义性。基类成员有相同的名称。
  • 路径二义性。如果派生类所继承的多个基类有相同的基类,而派生类对象需要调用这个祖先类的接口方法,产生路径二义性。

35. 内联函数声明在头文件,定义在cpp文件,不能内联,因为编译器“看不见”函数主体。实际上,若内联函数要供多于一个模块使用,必须定义与头文件中。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • <center>C++ 笔记 - 基础语法篇</center> this 指针 在 C++ 中,每一个对象都能通过...
    MacLeon阅读 393评论 0 0
  • C++ 是 C 语言的超集,它是一种使用非常广泛的计算机编程语言。C++ 作为一种静态数据类型检查的、支持多范型的...
    神齐阅读 882评论 0 1
  • cpp技术知识点笔记: 1:list 和 vector的实现区别? list空间按需分配,是链表类型,内存不一定连...
    小熊之怒阅读 217评论 0 0
  • 1. 让自己习惯C++ 条款01:视C++为一个语言联邦 为了更好的理解C++,我们将C++分解为四个主要次语言:...
    Mr希灵阅读 2,792评论 0 13
  • 20190228暂停学习,后续继续 第一部分让自己习惯C++ 条款1:视C++为一个语言联邦 C++最初的名称C ...
    去年匆匆今年匆匆阅读 405评论 0 1