C++msbs

2021-02-02 07:16

阅读:667

标签:临时对象   复制   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(vectorint>::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) {
    coutendl;
}

 

(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

  保存在栈中,编译时创建固定大小的数组

arrayint,4> a={1,2,3,4};

int len=4;
arrayint,len> b={1,2,3,4};  //错误,编译期间需要能确定大小

 

  不能隐式转换为指针:

void fun(int *p,int n)
{
    for(int i=0;i) {
        coutendl;
    }
}

arrayint,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":"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)会抛出异常

 

C++msbs

标签:临时对象   复制   set   deque   img   属性   表示   iterator   rtu   

原文地址:https://www.cnblogs.com/taoXiang/p/12796556.html

上一篇:python函数递归-实例

下一篇:Java中的锁


评论


亲,登录后才可以留言!