C++常见知识点总结

常见字符

 *

  1. 注释:/* 这是一个注释*/
  2. 乘法:a * b
  3. 取值运算符(间接值/解除引用/解引用运算符):*指针变量,int a = 4;int *p=&a; *p
  4. 指针变量:数据类型 *变量名, int *no = &bh,代表了no是一个指针
  5. 数据类型:int*:整型指针类型、char* 字符指针类型,char* c=(char *) &b。int no=38,int* ptr=&no, &no和ptr是一个东西,no和*ptr是一个东西。

注意:int *no 和 int* no什么区别

 1. 实际上没有区别
 2. int* a, b; 这里 a 是指向 int 的指针,而 b 只是一个普通的 int 整数变量。
 3. int *a, *b; 这样的声明清晰地表明 a 和 b 都是指向 int 的指针。

::

  1. 变量属于哪个域:std::count<<"no"
  2. 当局部变量和全局变量名称相同时,会屏蔽全局变量使用局部变量,如果想使用全局变量,使用::,:: a。

常见关键词 

typedef

数据类型的别名

static(静态)

静态局部变量

  1.  主要作用:会影响变量的存储期和作用域。
  2. 只会被初始化一次
  3. 静态局部变量:static修饰的局部变量生命周期和程序相同。即在函数调用结束后不会被销毁,而是保持其值直到下次调用。
  4. 静态类成员:用 static 声明的类成员属于整个类,而不是类的各个实例。

const(常量)

  1. 主要作用:增强程序的安全性
  2. 初始化之后,值不能被修改
  3. 当 const 修饰类的成员函数时,表示该函数不会修改类的任何成员变量

指针

  1.  常量指针:const 数据类型 *变量名;不能通过解引用的方法修改内存地址中的值(用原始的变量名是可以修改的)。
    1. Int a=3,b=4;

      const int* p=&a; #报错

      *p = 13; p=&b; #不报错

  2. 指针常量
    1. 数据类型 * const 变量名;
    2. 指向的变量(对象)不可改变。
    3. 在定义的同时必须初始化,否则没有意义。
    4. 可以通过解引用的方法修改内存地址中的值。
    5. Int a=3,b=4;

      const int* p=&a; #不报错

      *p = 13; p=&b; #报错

    6. 新名字:引用

  3. 常指针常量

    1. const 数据类型 * const 变量名;

    2. 指向的变量(对象)不可改变,不能通过解引用的方法修改内存地址中的值。

    3. 新名字:常引用。 

    4. 常量指针:指针指向可以改,指针指向的值不可以更改。

      指针常量:指针指向不可以改,指针指向的值可以更改。

      常指针常量:指针指向不可以改,指针指向的值不可以更改。

      记忆秘诀:*表示指针,指针在前先读指针;指针在前指针就不允许改变。

      常量指针:const 数据类型 *变量名

      指针常量:数据类型 * const 变量名

 void

  1. 函数的返回值用void,表示函数没有返回值。
  2. 函数的参数填void,表示函数不需要参数(或者让参数列表空着)。
  3. 函数的形参用void *,表示接受任意数据类型的指针。
  4. 注意:
    1. 不能用void声明变量,它不能代表一个真实的变量,但是,用void *可以。
    2. 不能对void *指针直接解引用(需要转换成其它类型的指针)。
    3. 把其它类型的指针赋值给void*指针不需要转换。
    4. 把void *指针赋值给把其它类型的指针需要转换。

内存

内存分为内核空间和用户空间,内核空间由操作系统管理,与程序员没什么关系。我们写的程序运行在用户空间。一个c++源程序编译成可执行程序后,二进制文件的大小是固定的,最多几十兆。

程序运行时,内存主要分成四个区,分别是栈、堆、数据段和代码段。

1)栈:存储局部变量函数参数和返回值。不管计算机的内存是8G还是16G,分配给栈的只有几兆。修改系统参数可以调整栈的大小。

2)堆:存储动态开辟内存的变量。内存越大,分配的内存就越大。

3)数据段:存储全局变量和静态变量。

4)代码段:存储可执行程序的代码和常量(例如字符常量),此存储区不可修改

注意:栈和堆的主要区别:

1)管理方式不同:栈是系统自动管理的,在出作用域时,将自动被释放;堆需手动释放,若程序中不释放,程序结束时由操作系统回收。

2)空间大小不同:堆内存的大小受限于物理内存空间;而栈就小得可怜,一般只有8M(可以修改系统参数)。

3)分配方式不同:堆是动态分配;栈有静态分配和动态分配(都是自动释放)。

4)分配效率不同:栈是系统提供的数据结构,计算机在底层提供了对栈的支持,进栈和出栈有专门的指令,效率比较高;堆是由C++函数库提供的。

栈的效率非常高,堆是用链表来管理的,效率会低一点。

5)是否产生碎片:对于栈来说,进栈和出栈都有着严格的顺序(先进后出),不会产生碎片;而堆频繁的分配和释放,会造成内存空间的不连续,容易产生碎片,太多的碎片会导致性能的下降。

6)增长方向不同:栈向下增长,以降序分配内存地址;堆向上增长,以升序分配内存地址。

空指针

  1. 用0或NULL都可以表示空指针。int* p=0,表示还没有指向任何地址。
  2. 如果对空指针使用delete运算符,系统将忽略该操作,不会出现异常
  3. 用0和NULL表示空指针会产生歧义,C++11建议用nullptr表示空指针,也就是(void *)0。

野指针

出现野指针的情况主要有三种:

1)指针在定义的时候,如果没有进行初始化,它的值是不确定的(乱指一气)。

2)如果用指针指向了动态分配的内存,内存被释放后指针不会置空,但是,指向的地址已失效。

3)指针指向的变量已超越变量的作用域(变量的内存空间已被系统回收),让指针指向了函数的局部变量,或者把函数的局部变量的地址作为返回值赋给了指针。

规避方法:

1)指针在定义的时候,如果没地方指,就初始化为nullptr。

2)动态分配的内存被释放后,将其置为nullptr。

3)函数不要返回局部变量的地址。

注意:野指针的危害比空指针要大很多,在程序中,如果访问野指针,可能会造成程序的崩溃。是可能,不是一定,程序的表现是不稳定,增加了调试程序的难度。

函数指针

  1. 声明函数指针;
    1. 函数指针的声明是:

      int (*pfa)(int,string);

      bool (*pfb)(int,string);

      bool (*pfc)(int);

      pfa、pfb、pfc是函数指针名
  2. 让函数指针指向函数的地址;
    1. 函数指针的赋值:函数指针名=函数名;

  3. 通过函数指针调用函数。
    1. (*函数指针名)(实参); C

      函数指针名(实参); C++

数组

初始化:

1)数据类型 数组名[数组长度] = { 值1,值2,值3, ...... , 值n};

2)数据类型 数组名[ ] = { 值1,值2,值3, ...... , 值n};

3)数据类型 数组名[数组长度] = { 0 };  // 把全部的元素初始化为0。

4)数据类型 数组名[数组长度] = { };    // 把全部的元素初始化为0。

数组相关函数

sizeof(数组名):可以得到整个数组占用内存空间的大小

memset():把数组中全部的元素清零。void *memset(void *s, int c, size_t n);

memcpy():把数组中全部的元素复制到另一个相同大小的数组。头文件#include <string.h>。函数原型:void *memcpy(void *dest, const void *src, size_t n);

数组的地址

a)数组在内存中占用的空间是连续的。

b)C++将数组名解释为数组第0个元素的地址。

c数组第0个元素的地址和数组首地址的取值是相同的。

double a[5];
cout << "a的值是:" << (long long) a << endl;
cout << "&a的值是:" << (long long)&a << endl;
cout << "a[0]的地址是:" << (long long) &a[0] << endl;
/*这三个值一样*/

指针的数组表示

在C++内部,用指针来处理数组。

C++编译器把   数组名[下标]  解释为  *(数组首地址+下标)

C++编译器把   地址[下标]  解释为  *(地址+下标)

int a[5] = [3,5,6,7,8,9,10,11];
int *p = a;

for(int ii=0; ii<5;ii++)
{
    count<<"a["<<ii<<"的值是:"<<a[ii]<<end;
    count<<"*(p+"<<ii<<")的值是:"<<*(p+ii)<<end;
    count<<"p["<<ii<<"的值是:"<<p[ii]<<end;
}

一维数组的排序qsort

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

  1. 为什么需要第三个形参size_t size
    1. 在函数内部不是按照函数类型操作的,而是按照内存块来操作的,交换两个元素,不是用赋值,而是memcpy()函数。
  2. size_t是C标准库中定义的,在64位系统中是8字节无符号整型(unsigned long long)。typedef unsigned long long size_t

动态分配内存new

用new动态创建变量

  1. 申请内存的语法:new 数据类型(初始值);  new int(5) // C++11支持{}
  2. 如果动态分配的内存不用了,必须用delete释放它,否则有可能用尽系统的内存。

用new动态创建一维数组

1)数组的内存:普通数组在栈上分配内存,栈很小;如果需要存放更多的元素,必须在堆上分配内存。

2)创建:动态创建一维数组的语法:数据类型 *指针=new 数据类型[数组长度];

3)释放:释放一维数组的语法:delete [] 指针;

注意:

  1. 不要用delete[]来释放不是new[]分配的内存。
    1. C语言的malloc()函数也可以动态分配内存,malloc()函数分配的内存要用free()函数来释放,不能用delete。
    2. 指针指向的地址是栈上的变量,普通变量的地址也不能用delete。int a[1000]; a[1000]=8;
  2. 不要用delete[]释放同一个内存块两次(否则等同于操作野指针)。
  3. 对空指针用delete[]是安全的(释放内存后,应该把指针置空nullptr)。
    1. 不写[],只会释放第0个元素的内存空间
  4. 如果内存不足,调用new会产生异常,导致程序中止;如果在new关键字后面加(std::nothrow)选项,则返回nullptr,不会产生异常。int a[100000]; a[100000]=8;内存不足,会崩溃掉。int *a = new int[1000000000]; a[10000000000]=8;内存不足,会崩溃掉。
for (int ii = 1; ii > 0; ii++)
	{
		int* p = new int[100000];     // 一次申请100000个整数。
		cout << "ii="<<ii<<",p=" << p << endl;
	}

内存会螺旋式越来越大,(一会大一会小,是因为系统有交换区)

C风格的字符串

  1. string是C++的类,封装了C风格的字符串。
  2. string能自动扩展,不用担心内存问题。
  3. C语言约定:如果字符型(char)数组的末尾包含了空字符\0(也就是0),那么该数组中的内容就是一个字符串。

  4. 一个中文汉字占两个字节。
  5. string str='XYZ', str[0]显示字符,int(str[0]) 显示ascii码。

初始化

char name[11]; // 可以存放10个字符,没有初始化,里面是垃圾值。

char name[11] = "hello"; // 初始内容为hello,系统会自动添加0

char name[] = { "hello" }; // 初始内容为hello,系统会自动添加0,数组长度是6。

char name[11] = { "hello" }; // 初始内容为hello,系统会自动添加0

char name[11] { "hello" }; // 初始内容为hello,系统会自动添加0。C++11标准。

char name[11] = { 0 }; // 把全部的元素初始化为0。

注:

char name[11];

count<<"name="<<name<<end;

11个字节,一个汉字是两个字节,应该显示5个烫,为什么显示这么多?

不管是C还是C++,处理字符串的时候,从字符数组的起始地址开始,遇到0才结束。没遇到0就继续找。

相关函数

  1. memset(name,0,sizeof(name));   // 把全部的元素置为0。

  2. strcpy()字符串复制或赋值。

    char *strcpy(char* dest, const char* src);

    功 能: 将参数src字符串拷贝至参数dest所指的地址。注:C++风格的字符串可以用等号赋值,C风格的字符串不能用等号赋值,要用strcpy。

  3.  strncpy()字符串复制或赋值

  4. strlen()获取字符串的长度

  5. strcat()字符串拼接

  6. strncat()字符串拼接

  7. strcmp()和strncmp()字符串比较

  8. strchr()和strrchr()查找字符

  9. strstr()查找字符串

  10. 注意事项:

    1. 操作字符串的函数,遇到0字符串才结束,不会判断数组是否越界,因为操作字符串的函数行参是指针,只存放了字符串的起始地址,没有数组长度参数。

    2. 不要在子函数中对字符指针用sizeof运算,因为得到的是指针占用内存空间的大小,都是8字节,不是数组的大小。所以,不能在子函数中对传入的字符串进行初始化,除非字符串的长度也作为参数传入到了子函数中。

二维数组

行指针(数组指针)

声明行指针的语法:数据类型 (*行指针名)[行的大小];  // 行的大小即数组长度。

int (*p1)[3];         // p1是行指针,用于指向数组长度为3的int型数组。

double (*p3)[5];  // p3是行指针,用于指向数组长度为5的double型数组。

注:

int (*p2) [5]: 行指针

int* p2[5] 指针数组

结构体

函数

  1. 清空结构体:memset(&stgirl, 0, sizeof(stgirl));可以把结构体中全部的成员清零。
  2. 清空结构体:bzero(&stgirl, sizeof(stgirl));可以把结构体中全部的成员清零。
  3. 复制结构体:memcpy:把结构体中全部的元素复制到另一个相同类型的结构体.
  4. 复制结构体:=

链表

  1. 都是动态分配内存,没有静态一说。

重载(派生类使用基类函数

继承

  1. 友元函数不是类成员,不能继承。

构造函数/析构函数

  1. 构造函数不能继承,创建派生类对象时,先执行基类构造函数,再执行派生类构造函数。

  2. 析构函数不能继承,而销毁派生类对象时,先执行派生类析构函数,再执行基类析构函数。

构造基类

  1. 基类构造函数负责初始化被继承的数据成员(基类成员变量必须有基类的构造函数初始化);派生类构造函数主要用于初始化新增的数据成员。
    1. 基类的私有成员在派生类不可见,派生类没办法初始化基类的私有成员。
    2. 所有派生类构造函数都初始化基类数据,属于重复,不符合继承的理念。
  2.   

访问权限

公有继承
各成员派生类中基类与派生类外
基类的公有成员直接访问直接访问
基类的保护成员直接访问调用公有函数访问
基类的私有成员调用公有函数访问调用公有函数访问
从基类继承的公有成员直接访问直接访问
从基类继承的保护成员直接访问调用公有函数访问
从基类继承的私有成员调用公有函数访问调用公有函数访问
派生类中定义的公有成员直接访问直接访问
派生类中定义的保护成员直接访问调用公有函数访问
派生类中定义的私有成员直接访问调用公有函数访问
私有继承

第一级派生类中第二级派生类中基类与派生类外
基类的公有成员直接访问不可访问不可访问
基类的保护成员直接访问不可访问不可访问
基类的私有成员调用公有函数访问不可访问不可访问

多态(基类使用派生类函数)(基类指针可以指向派生类对象)

  1. 基类指针只能调用基类的成员函数,不能调用派生类的成员函数。

  2. 有了virtual虚函数,基类指针指向基类对象时就使用基类的成员函数和数据,指向派生类对象时就使用派生类的成员函数和数据,

virtual/虚函数

  1. 在基类的成员函数前加virtual 关键字,把它声明为虚函数,基类指针就可以调用派生类中同名的成员函数,通过派生类中同名的成员函数,就可以访问派生对象的成员变量。
基类调用派生类情况
virtual函数基类指针指向派生类对象,调用派生类函数
virtual函数

基类指针指向派生类对象,加基类的域,调用基类函数

virtual函数基类指针指向派生类对象,调用基类函数

纯虚函数

  1. 纯虚函数只有函数名、参数和返回值类型,没有函数体,具体实现留给该派生类去做。

抽象类

  1. 含有纯虚函数的类被称为抽象类,不能实例化对象,可以创建指针和引用。

  2. 派生类必须重定义抽象类中的纯虚函数,否则也属于抽象类。

dynamic_cast

运行阶段类型识别。语法:派生类指针 = dynamic_cast<派生类类型 *>(基类指针);

dynamic_cast只适用于包含虚函数的类。因为要查虚函数表。

final

  1. 限制某个类不能被继承
  2. 或者某个虚函数不能被重写

override

  1. 重写基类的虚函数

#pragma once

#pragma once用来防止某个头文件被多次include。只要在头文件的最开始加入这条指令就能够保证头文件被编译一次。

#ifndef,#define,#endif用来防止某个宏被多次定义。

内联函数

在C++中,内联函数可代替有参数的宏,效果更好。

宏和const

结构体和类

STL

逻辑结构物理结构底层
string
list链表
vector数组
pair
map
unordered map数组+链表
queue队列数组或链表
array静态数组
deque双端队列
forward_list单链表
multimap红黑树
set&multiset

红黑树

unordered_multimap
unordered_set&unordered_multiset
priority_queue有权值的单向队列queuedequelist
stackdequelist

vector

封装的是数组

list

封装的是链表

pair

  1. 类模板
  2. 实现是结构体
  3. make_pair()返回的临时对象。//auto p5 = make_Pair<int, string>(5, "西施5");  

红黑树

  1. 如果数据量很小,数组+二分查找法;如果数据量很大(几万),用红黑树。;如果数据量达到了上千万

map

  1. 封装了红黑树(平衡二叉排序树)
  2. ​​​​​​​map容器的元素是pair键值对。

unordered_map哈希表

  1. 容器封装了哈希表
  2. 数组+链表
  3. 效率
    1. 插入不需要比较
    2. 删除元素与查找元素的效率相同

queue

  1. queue容器的逻辑结构是队列,物理结构可以是数组或链表
  2. queue容器不支持迭代器。

智能指针

unique_ptr

  1. 独享它指向的对象
  2. 因此没有赋值和拷贝功能,因为要独享。
  3. 不要用同一个裸指针初始化多个unique_ptr对象。

shared_ptr

  1. 共享它指向的对象
  2. make_shared:std::make_shared 是一种用于创建 std::shared_ptr 的便利函数

weak_ptr

裸指针 vs智能指针

  1. 裸指针
    1. 内存泄漏:内存没有被正确释放
    2. 悬空指针(dangling pointer):指针所指向的内存被释放后,指针未置为 nullptr
    3. 多次释放等:同一个指针被多次释放,通常会导致程序崩溃。
  2. 智能指针

auto

  1. 自动推导类型

explicit

  1. 关闭类型自动转换的特性

常见知识点

运行C++程序

Mac运行cpp程序:g++ -o demo demo.cpp

标准

  1. C++98
  2. C++11现在用的比较多
  3. C++14:落地要过几年

内存

位置
智能指针

  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值