c++笔记

cpp技术知识点笔记:
1:list 和 vector的实现区别?
list空间按需分配,是链表类型,内存不一定连续,在插入和删除的时候不会引起迭代器失效,删除元素只有当前元素的迭代器失效
vector的空间是连续的,空间不足的情况下,(1)开辟新的大内存空间;(2)从旧的空间拷贝数据到新的内存空间;(3)释放旧的空间
vector的内部空间,总是供大于需,避免频繁的数据移动操作,size = 2*size 插入和删除可能引起迭代器失效。
vector的删除操作引起迭代器失效:删除可能导致某个迭代器访问不再合法、然后继续访问就有可能访问已经失效的空间。
2:指针和引用?
指针有空间,存储的是一个地址。引用只是变量别名。
指针可以为NULL,引用不可以为空。
指针可以在初始化之后,改变指向,引用一旦初始化将不能改变。
存在const修饰的指针,不可以改变指向,不存在const修饰的引用。
指针可以有二级操作,**p 引用没有。
指针指向的变量,需要解引用,引用直接访问就行。
指针和引用自增的含义不同。
3:define和const区别?
define定义的是一个字符串,没有类型,没有存储器,编译器不对其进行安全检查,const有类型,存在数据段,可以进行安全检查。
define不可以调试,const可以调试。
define在预处理时进行替换,const在编译时进行。
4:char arr[10]; 和 char *p = new char[10] 区别?
char arr[10]; 字符数组未初始化 ,存放在未初始化的的数据区,如果存在static修饰,则存放在静态数据区。
new char[10]; 定义的字符数组,使用'/0'进行初始化,存放在堆中。
5:c++的内存分配方式?内存分布图?
new、malloc分配空间,需要手动释放。
系统在栈上分配空间,函数调用的参数信息,系统释放。
static静态存储分配空间。


未初始化数据段
初始化数据段
代码段
6:自实现String类:?
class String
{
public:
String(const char *str);
String(const String &other);
String& operator=(const String &other);
~String();
private:
m_data;
m_size;
}
String::String(const char* str)
{
if(str == nullptr)
{
m_data = https://www.it610.com/article/new char[1];
m_data[0] = '\0';
m_size = 0;
}
else
{
m_size = strlen(str);
m_data = https://www.it610.com/article/new char[m_size + 1];
strcpy(m_data, str);
}
}
String::String(const String& other)
{
m_size = other.m_size;
m_data = https://www.it610.com/article/new char[m_size + 1];
strcpy(m_data, other.m_data);
}
String::String& operator=(const String& other)
{
if(this == &other)
{
return *this;
}
delete[] m_data;
m_size = strlen(other.m_data);
m_data = https://www.it610.com/article/new char[m_size + 1];
strcpy(m_data, other.m_data);
return *this;
}
7:extern关键字?
置于变量或者函数前,表示变量或者函数定义在别的文件中。
extern 和 C连用的时候,告诉编译器编译时候用c的方式编译。
当不与c连用的时候,作用声明函数或者全局变量的作用范围。
8:static关键字?
1.修饰局部变量:存放在静态变量区,声明周期和main函数相同,main函数之前初始化,函数结束退出。
2.修饰全局变量:全局变量原本就存放在静态区,static修饰,使其只能被包含该定义的文件访问,修改了其作用域。
3.修饰函数:static修饰的函数,只能在包含改定义的文件中调用,静态函数,声明和定义需要在同一个文件中。
4.修饰成员变量:使其成为类的全局变量,所有类对象共享,包括派生类,只能在类外进行初始化。可以使用const修饰在类内进行初始化。
静态数据成员,实体远在main函数开始前已经在全局数据段中诞生了。生命周期和类对象是异步的。这个时候类对象还没有开始,但是静态数据成员已经可以访问了。为了此所以一定要在类外初始化。
5.成员函数:只存在一份该函数,所有对象共享,不含this指针,无需创建任何对象就可以访问。
不可以同时使用const和static修饰成员函数,原因c++在实现const修饰的函数时候,会在函数中隐式添加一个const this*,static是没有this指针的,互相矛盾。
9:volatile作用?
修饰变量,表名某个变量随时可能会被外部改变,改变量不能被缓存到寄存器,每次需要重新读取。
10:const的作用?
1.修饰变量,只读变量,不可以被更改。
2.修饰函数参数(返回值,一般很少用),参数内容不能被修改
3.修饰指针,指针所指对象不能被改变。int* const p = &a; 指针p指向a的地址,不能再被改变。
4.修饰指针所指的变量,int const *p = &a; const修饰*p, *p所指的内容不能被改变。
5.修饰类成员函数,表明是一个常函数,不能修改类的成员变量。
一个成员函数不会修改成员值,最好将其修饰为const函数,void func() const;
11: new和malloc?
malloc和free是C语言的标准库函数,new和delete是c++的运算符,都可以进行申请内存和释放内存。
对于非内部数据结构的类型而言,malloc和free无法满足动态对象要求。
new可以认为是malloc加构造函数的执行,new出来的指针是直接带构造信息的,malloc返回的都是void指针。
12:c++多态?
一个接口,多种方法,c++多态主要通过虚函数实现的,虚函数允许子类从写overrid。
编译时多态:通过重载函数实现。
运行时多态:通过虚函数实现。
13: c++的虚函数 纯虚函数?
纯虚函数是在基类中声明的虚函数,基类中没有定义,函数原型后加=0,纯虚函数的实现起到规范作用,继承的类必须按照这个形式自己实现。
虚函数,运行时多态,父类提供虚函数实现,子类可以重写父类虚函数实现子类的特殊化。
c++包含纯虚函数的类是抽象类,抽象类不能new出对象。只有实现了纯虚函数的子类,才可以。
14: 静态绑定和动态绑定?
静态绑定发生在编译期,动态绑定发生在运行期。
动态类型可以更改,静态类型不可以更改。
继承体系中,只有虚函数使用的是动态绑定,其它使用的都是静态绑定。
静态多态使用的是模板技术,或者是函数重载技术。动态多态通过虚函数实现在运行期绑定的技术。
15:值传递和引用传递?
值传递是指当函数发生函数调用时候,给形参分配空间,用实参来初始化形参,这一过程是参数值得单项传递。形参不影响实参。
引用传递,将引用作为形参,系统初始化时,自动使用实参来初始化形参,形参是实参的一个别名,直接操作的是实参。
16: 内联函数?特点?
将inline放在函数声明前不起任何作用,必须要放在定义函数前。
使用关键字inline的函数,编译器在调用处,使用函数体进行替换,节省参数传递,控制转移开销。
内联函数不能有循环,和switch语句;
内联函数定义,必须出现在第一次调用之前;不能有异常声明。
17: 拷贝构造函数和 operator=?
前者属于构造函数,后者属于运算符重载。
对于在赋值操作之前,还没有构造的变量,调用拷贝构造函数。
赋值之前,已经构造的变量,调用赋值操作。
class_obj a("xxx"); class_obj b = a; //拷贝构造 class_obj c; c = a; // 赋值operator=
18: 友元函数?
friend修饰,不可以继承,不可以传递,可以访问相应类的公有,私有,受保护的成员。
19: 抽象类?
带有纯虚函数的类叫抽象类。
纯虚函数:基类中仅仅给出声明,不对虚函数实现定义,而是在派生类中实现,该函数成为纯虚函数。
抽象类的析构函数应被声明为virtaul,因为涉及到继承。
20: 虚构造函数?虚析构函数?
不能声明虚构造函数,多态是不同对象对同一消息的不同特性,虚函数作为运行多态的基础,主要针对对象,构造函数是在对象生成之前运行的,没有意义。
可以声明为虚析构函数,析构函数是对象释放的一些清理工作,一个类的析构函数是虚函数,由它派生而来的所有子类也是虚函数,析构函数设置为虚函数,就可以
调用适当的析构函数对不同的对象进行清理工作。
21: 函数重载和函数重写?
重载:同名不同参的函数,互相被称为重载函数。
条件:1.名必须相同,2.参数必须不同,3.返回值都可。
重写:也可叫覆盖,继承中,子类重新定义父类中有相同名称和参数的虚函数。
条件:1.两个函数都必须是虚函数,一个在基类,一个在派生类。2.函数名和参数必须一致。3.返回值相同。
22: class和struct?
默认成员访问权限不同,struct是公有访问,class是私有访问。
默认继承方式不同,struct是公有继承, class是私有继承。
定义模板时可以使用class,不能使用struct。
都可以包含成员函数,实现继承,都可以实现多态。
23: 浅拷贝和深拷贝?
对一个对象进行拷贝,默认是复制构造拷贝,两个对象的指针成员所指内存相同。只对指针进行拷贝。
编译器在我们没有定义拷贝构造函数时候,采用默认拷贝构造函数,进行浅拷贝,两个指针指向同一个内存空间。
深拷贝不但对指针进行拷贝,而且对指针所指内容也进行了拷贝,深拷贝的内容两份,浅拷贝内容一份。
24: 析构函数带virtual?
基类派生,析构函数一定要带virtaul关键字,基类指针指向派生类对象,删除该指针时,并不会释放派生类对象的空间,不会调用派生类析构函数。
不需要基类指针指向派生类对象,可以不用带virtaul。
25: delete 和 delete[]?
delete释放内存,delete ptr释放ptr指向的内存。
delete[] 释放内存,逐一调用数组的每个对象的析构函数,全部释放。
26: 显示转换?
static_cast: 基本类型之间的数据转换。
const_cast: 将const类型的指针转换为非const类型的指针。
reinterpret_cast: 不相关类型之间的转化,目标和原始之间有相同的位数。
dynamic_cast: 运行时检查转换是否安全,用于类层次间的上行和下行转化。
27: 运算符重载?
实际上是函数重载,改变运算符的操作方式,使其可以适用类类型,为类提供一个接口,使语言面向问题而不是面向程序。
增强c++的扩展性。
28: stl?
c++提供的标准模板库。
组成:容器,迭代器,算法,函数对象适配器。算法位于核心。
29: auto?
const 和 volatile c++的两种属性
声明为auto的变量不能从初始表达式中带走cv(上面那两种)限制符
const int a = 1; auto b = a; // b int类型auto &c = a; // c是const int类型
decltype则可以带走cv限制符。
但是,如果是一个对象的定义中含有const或volatile限制符,使用decltype进行推导时候,其成员不会继承cv的限制。
30: 预处理->编译->汇编->链接?
1 预编译 过程主要处理那些源文件中的以“#”开始的预编译指令,主要处理规则有:
1.将所有的“#define”删除,并展开所用的宏定义
2.处理所有条件预编译指令,比如“#if”、“#ifdef”、 “#elif”、“#endif”
3.处理“#include”预编译指令,将被包含的文件插入到该编译指令的位置,注:此过程是递归进行的
4.删除所有注释
5.添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息以及用于编译时产生编译错误或警告时可显示行号
6.保留所有的#pragma编译器指令。
2 编译 过程就是把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。这个过程是整个程序构建的核心部分
3 汇编 器是将汇编代码转化成机器可以执行的指令,每一条汇编语句几乎都是一条机器指令。经过编译、链接、汇编输出的文件成为目标文件。
4 链接 的主要内容就是把各个模块之间相互引用的部分处理好,使各个模块可以正确的拼接。 链接的主要过程包块 地址和空间的分配、符号决议和重定位等步骤。
31: 静态链接和动态链接?
静态:静态链接的时候,载入代码就会把程序会用到的动态代码或动态代码的地址确定下来。
动态:直到真正调用动态库代码时,载入程序才计算(被调用的那部分)动态代码的逻辑地址,但运行期间的性能比不上静态链接的程序。
32: 构造函数存在自动转换功能:
声明构造函数的时候前面添加上explicit即可,这样就可以防止这种自动的转换操作。
33: 关键字registr,typdef的作用?
regist尽可能让变量保存在cpu的寄存器中,减去cpu从内存抓取效率,提高运行效率。
注意:
局部变量才可以被修饰register
不能取地址,地址存放在内存,但是它在寄存器。
一定是cpu所接受的类型。
typedef:起一个新名字。
提高移植性。
提高编码效率。
解释数据类型作用。
34:explicit关键字的作用?
编译器有记忆功能,为的是将频繁使用到的变量,保存在寄存器中,提高效率。
但是如果该变量在内存中被改动了,寄存器中的值就不再有效,但是计算器不知道,还是会使用寄存器中的值。
【c++笔记】解决此办法就是加上关键字explicit关键字,提醒cpu每次从内存中取改值。

    推荐阅读