标签:临时对象 复制 set deque img 属性 表示 iterator rtu
1. 虚函数与纯虚函数
虚函数是允许被派生类重新定义的成员函数
virtual 返回类型 func(参数); virtual 返回类型 func(参数)=0;
虚函数是为了实现动态绑定(基类能创建自己的对象)
纯虚函数使一个类称为抽象类(不能创建对象的类,即这个类只是作为一个模型出现),派生类只继承这个接口
2. 基类析构函数声明为虚函数
为了防止内存泄漏。通过父类指针去销毁子类对象时,如果析构函数不是虚函数,则不会触发动态绑定,只调用基类析构函数回收了基类对应的空间,派生类的析构函数没有被调用。
3. 自增运算符
i++:创建临时变量存储i的值,i增加1,然后返回临时变量的值
++i:i增加1,返回这个i(并且这个表达式是左值)
4. vector
(1)size和capacity
size指的是当前实际元素个数
capacity指的是vector中能容纳的元素个数
(2)
reserve让容器预留空间,增加了vector的capacity,没有改变size
resize需要两个参数(容器新的大小,新插入元素),如果没有第二个参数,就用默认构造函数插入这个元素,vector的size被改变了
5. 关于类里面 const、static
static对变量产生的作用:
对局部变量,改变了其存储位置和生命期。存储位置从栈中变成静态存储区,其生命原来是随着语句块结束而消失,加了static则在整个程序运行期间都存在。但其作用域是没有改变的。
对全局变量,改变了其作用域,使得该变量只在本文件内可见
(1)static数据成员的初始化
static数据成员存储在静态存储区上, 而不是栈上
static数据成员的初始化要在类体外面(静态成员属于整个类,而不属于某个对象,如果在类里面初始化,会导致每个对象都包含这个静态成员)
不能在头文件中初始化(会导致重复定义)
static变量在main函数之前初始化(普通全局变量、静态全局变量、类静态成员变量都在main之前初始化)
static成员可以成为成员函数的参数
(2)const数据成员
const成员变量意思是对每个对象而言,它是常量且每个对象的这个值可以不同,但对类而言是可变的
const成员变量只能用构造函数初始化列表的方式初始化
(3)static const数据成员
static const整型可在类内初始化
static const其他类型包括浮点等要在类外初始化
(1)static修饰成员函数
该成员函数没有了this指针,只操作static变量,与对象没有关系
(2)const修饰成员函数
表示该函数不会修改对象的数据(通过const this *)
(3)不能用static和const同是修饰成员函数
因为static函数没有this指针,所以同时用两个修饰没有意义,编译器也会报错
-----
数据成员初始化顺序:
列表初始化方式:与定义成员变量的顺序有关
const成员变量必须在构造函数初始化列表中初始化
类的static成员变量,只能在类外初始化
基类、派生类成员变量初始化:
基类静态变量、全局变量
派生类静态变量、全局变量
基类成员变量
派生类成员变量
6. 指针和引用
(1)
指针是一个新的变量,存储另一个变量的地址。是间接操作另一个变量
引用不是变量,只是一个别名,对引用的操作是直接操作在该引用关联的变量上
(2)
就指针而言,有指向常量的指针 int const *p ,有指向不可变的指针常量 int *const p
引用只有指向常量的引用 int const &a=b 而引用的指向本来就不可变
(3)
指针有多级指针,引用只有一级
指针可不初始化,引用必须初始化
sizeof指针,得到指针变量所占空间,一般4字节 sizeof引用,得到其指向变量/对象大小
自增运算符运算意义不一样
7. 多态
含义:一个接口,多种方法
目的:接口重用【封装:模块化 继承:代码扩展】
(1)静态多态/早绑定
函数重载:函数名一样,返回值、参数个数、类型不一样
编译期间确定调用哪个
(2)动态多态/晚绑定
通过虚函数实现
运行时才确定调用哪个
用基类指针调用虚函数,根据指针实际指向对象类型调用相应的函数
(3)虚函数表
类有虚函数时,则类会有一张虚函数表(表的每一项是一个虚函数的地址)
类的每个对象有一个指向虚函数表的虚指针(位于对象地址前面)
派生类有兼容基类的虚函数表,如果重写了一个虚函数,则在其虚函数表里面对应位置的函数地址替换为重写的函数地址
8. new和malloc
new是运算符;malloc是库函数(一个语言的编译器能控制运算符)
new会调用构造函数,delete会调用析构函数; malloc、free不会
new返回具体类型的指针;malloc返回void *指针
new可被重载;malloc不能
9. C++内存分区
代码区:存放二进制代码
字符串常量区
全局和静态区:全局变量、静态变量存储
堆区:malloc、new从这里申请空间
栈区:存放局部变量
10. 常用容器
vector:基于数组
deque双端队列:类似数组加链表的组织形式,使用起来类似数组或队列
list双向链表、forward_list单向链表
array:固定大小数组
string
map、set:无重复、有序 基于红黑树(插入、删除效率高)
multimap、multiset:可重复、有序 基于红黑树(插入、删除效率高)
unordered_map、unordered_set:无重复、无序 基于哈希表(查找效率高)
unordered_multimap、unordered_multiset:可重复、无序 基于哈希表(查找效率高)
11. 内存泄漏
可能场景:
忘记释放动态申请的内存空间
基类析构未声明为virtual
系统资源忘记释放
解决:
重载new、delete
使用智能指针:shared_ptr、weak_ptr、unique_ptr
12. C++11新特性
(1)auto类型推导、decltype
用auto类型变量接收很复杂的类型:
for(vector<int>::const_iterator itr=vec.cbegin();itr!=vec.cend();++itr) for(auto itr=vec.cbegin();itr!=vec.cend();++itr)
auto不能用于函数传参
decltype为了解决auto只能对变量进行类型推导的不足出现的,可判断表达式的类型:
auto x=1; auto y=2.01; decltype(x+y) c;
(2)for循环
int a[5]={1,2,3,4,5}; for(int &x:a) { cout<<x<<endl; }
(3)lambda函数
[捕捉列表] (参数) mutable -> 返回值类型 {函数体}
捕捉列表能捕捉上下文中的变量供函数使用
mutable:取消默认的lambda的const属性,使函数体内能修改变量(不影响外部得到变量)
(4)override、final关键字
在某个应该被子类重写的函数后加上override,表明这个函数是重写的虚函数
在类名字后面加final或在函数后面加final,表明这个类不希望被继承,不希望这个函数被重写
(5)智能指针
将基本类型指针封装为类对象指针,并在其析构函数里编写delete语句删除指针指向的内存空间
(6)右值引用
例子:
string x = "abc";
string y = "def"; string a(x);
string b(x+y); string c(fun()); //fun函数返回一个字符串
第三行用x初始化a,则会调用相应的拷贝构造函数,进行深度拷贝
第四行用x+y初始化b,实际上创建了一个临时变量,然后用这个临时变量初始化b,但是这个临时变量随机就丢弃了
第五行用一个返回非引用类型的函数的返回值初始化c,也是用一个临时对象接收返回值,然后初始化c
上面x是左值
第四和第五行的参数是右值,表达式产生了string的匿名对象/临时对象,然后用临时对象初始化,然后丢弃临时对象,这样实际上多做了操作,通过右值引用简化流程。如下:修改构造函数,入参为右值引用
string (string && tmp) { 浅拷贝 }
通过浅拷贝,将临时对象的地址复制给b或者c,然后将临时对象指针置空,充分利用了这个临时对象,这个构造函数也叫 "转移构造函数"
(7)空指针nullptr
C中空指针常量NULL宏定义为0或 (void *)0,任意类型指针都可转换为 void *类型
C++中别的类型指针不能与void *类型指针转换,且NULL宏定义为 0
C++中0有了两个含义:整型数值0和NULL为了避免重载函数调用时二义性,定义了nullptr表示空指针来替代NULL
(8)线程支持
(9)构造函数
委托构造:可在一个构造函数后调用另一个重载的构造
(10)新增容器
①std::array
保存在栈中,编译时创建固定大小的数组
array<int,4> a={1,2,3,4}; int len=4; array<int,len> b={1,2,3,4}; //错误,编译期间需要能确定大小
不能隐式转换为指针:
void fun(int *p,int n) { for(int i=0;i<n;i++) { cout<<*(p+i)<<endl; } } array<int,4> a={1,2,3,4};
fun(a,4); //错误,a不能被隐式转换为指针 fun(&a[0],4); fun(a.data(),4);
②单向链表 forward_list
③无序容器四个
④元组tuple
固定大小的不同类型元素的集合
(11)正则表达式
string src[]={"aa.txt","a2.txt","123.txt","a334g.jpg"}; regex re(".*[0-9]\\.txt");//以数字结尾的txt for(auto &x:src) { cout<<x<<":"<<regex_match(x,re)<<endl; }
(12)其他
.....
13. const和define
(1)起作用时间不同
define在预处理阶段起作用,进行替换
const在编译阶段起作用
(2)类型检查
define不进行类型安全检查
const有数据类型,编译时进行类型检查
(3)内存空间
define不分配内存
const需要在静态存储区中分配空间
14. 悬空指针和野指针
悬空指针:所指向对象被释放,但指针变量的值未赋值空
野指针:没有初始化的指针变量,值不确定
15. sizeof和strlen
(1)功能
sizeof是运算符,用来计算一种类型所占内存大小
strlen是函数,参数为char *,计算以‘\0‘结尾字符串的长度,且不包括‘\0‘
(2)只有用字符数组比较这两个才有意义
char a[]="abc"; sizeof(a); //4 strlen(a); //3
16. 字节对齐
(1)结构体
比如int为4字节的情况下,一般是4字节对其
(2)联合体
联合体所占空间与最宽成员及其他成员都有关系:①大小足够容纳最宽的成员 ②大小能被其所有成员基本类型的大小整除
union u1{ int n; char a[11]; double b; }; union u2{ int n; char a[5]; double b; };
u1类型需要空间16字节
u2类型只要8字节
17. 类型转换
(1)
18. 计算类大小
class A{}; sizeof(A)==1
class A{virtual Fun(){}} sizeof(A)==4(32位) 8(64位)
class A{static int a;}; sizeof(A)==1
class A{int a;}; sizeof(A)==4
class A{static void fun(){}}; sizeof(A)==1
类中static声明的成员变量不计算类的大小中
19. 大小端
大端big endian:低地址存放高位字节
小端little endian:第地址存放低位字节
数0x12 34 56 78在内存中存放形式为:地址从左到右变大
大端:0x12 0x34 0x56 0x78
小端:0x78 0x56 0x34 0x12
20. C++中 * 和 & 同时使用
void fun(Node * &p);
参数是一个Node类型指针的引用,在函数里面,不仅可通过p的值改变其指向的内容,也可以改变实参的值
举例:一个单链表反转,head指针指向第一个结点,反转后需要其指向原链表最后一个结点
①通过指针的指针实现
Node * fun(Node **head) { if(NULL == *head || NULL==(*head)->next) { return *head; } Node *p1 = *head; Node *p2 = p1->next; Node *p3 = NULL; (*head)->next = NULL; while(p2 != NULL) { p3 = p2->next; p2->next = p1; p1 = p2; p2 = p3; } *head = p1; //修改实参的值 return p1; //或者返回该值 } Node *head = initList(); head = fun(&head); //通过传入指针的地址改变了实参head的值 //现在head是指向新的头结点,,即原链表的最后一个结点
②传入指针的引用
void fun(Node *&head) { if(NULL==head || NULL == head->next) { return; } Node *p1 = head; Node *p2 = p1->next; Node *p3 = NULL; head->next = NULL; while(p2!=NULL) { p3 = p2->next; p2->next = p1; p1 = p2; p2 = p3; } head = p1; } Node *head = initList(); fun(head);
21. C++中堆和栈比较
(1)数据结构中的堆结构和栈结构
栈:先进后出的结构
堆:有大根堆和小根堆。大根堆是根结点值最大,且根结点的两个子树也是堆。用来实现优先队列,保证队列最前面的数是最大或最小的,每次取走一个数,堆调整结构,使得最前面的依然是最大或最小的
(2)内存分配中的栈和堆
这里的堆不是数据结构中说的堆
①增长方向
栈向下(向较低地址)增长;堆向上(向较高地址)增长
②申请方式
栈由编译器自动管理;堆需要编写程序的人主动申请
③效率
栈分配较快;堆申请较慢
④大小限制
栈能获得的空间较小,且是连续的内存区域;堆能获得的空间较多,且不连续(链表管理)
22. 空类
编译器为空类自动生成 默认无参构造函数、默认拷贝构造函数、默认拷贝赋值运算符、默认析构函数
class A{}; class A{ public: A(); A(const A&); ~A(); A& operator=(const A&); };
23. 不能写成虚函数的函数
(1)普通函数
非成员函数的普通函数只能重载
(2)友元函数
友元函数不属于类的成员函数,不能被继承,不能写成虚函数
(3)构造函数
构造函数用来初始化对象里面的成员,而基类构造不知道派生类有哪些成员
(4)内联成员函数
内联函数在编译时展开,而虚函数在运行时绑定
(5)静态成员函数
编译时确定,无法动态绑定,不支持多态
24. 一个string类的简答实现
class string{ public: string(const char* str); string(cosnt string &other); ~string(); string & operator=(const string &other); private: char *data; };
//***********************// string::string(const char *str) { if(NULL == str) { data = new char[1]; *data = ‘\0‘; } else { int len = strlen(str); data = new char[len+1]; strcpy(data,str); } } string::string(const string &other) { int len = strlen(other.data); data = new char[len+1]; strcpy(data,other.data); } string::~string(){ delete [] data; } string::string& operator=(const string &other) { if(this==&other) { return *this; } delete [] data; int len = strlen(other.data); data = new char[len+1]; strcpy(data,other.data); return *this; }
25.分配内存的方法
(1)malloc
void *malloc(unsigned int size)
申请分配长度size字节的连续空间,空间内容没有初始化
(2)calloc
void *calloc(unsigned int num,unsigned int size)
申请分配num*size字节的空间,并初始化内存空间为0
(3)realloc
void *realloc(void *ptr,unsigned int size)
申请分配size字节空间,并把内存空间首地址赋值给ptr,不会初始化
(4)new
申请分配空间,调用构造函数实现初始化
26. vector中直接访问和at访问
vec[i]可能有越界风险 vec.at(i)会抛出异常
标签:临时对象 复制 set deque img 属性 表示 iterator rtu
原文地址:https://www.cnblogs.com/taoXiang/p/12796556.html