C++小点总结(一)
说明
C++之所以难学,主要是其标准和知识点比较繁杂;之前的C++学习偏向理论,本专栏将以每20个小问题为一篇文章,主要记录实战中遇到的问题,难易程度不同,可根据标题参考;
1、文件名后缀的作用?
一般项目中会包含多个.cpp(源文件)和.h(头文件);
那有一些后缀为.c .cc .cxx的文件是怎么来的呢?
答:是由于不同的编译器导致的,一般在GNU编译器下会有这些后缀;
.hpp后缀的作用是什么呢?
答:实际上是.h和.cpp文件二合一,用来减少整个项目的编译过程;
2、C++11后的一种大括号初始化?
在变量后接大括号可实现初始化:
int a{16};
int a[] {1,2 3, 4, 5}; // 中间的等号可省略
3、头文件防卫式声明是什么?
是为了防止头文件重复引用造成的重定义现象;
具体做法:在每个头文件中加入条件编译(不同头文件中标识符设置应当不同)
#ifndef __head1__
#define __head1__
// 代码段
#endif
4、引用需要怎么理解?
本质:引用就是别名的意思,起到一个标识符的作用;
一些注意点:
-
定义引用并不额外占用内存,可以理解成引用和原变量占用同一块内存;
-
定义引用的时候必须初始化,并且不能绑定常量;
5、在Visual Studio中查看变量快捷键?
在Debug模式下,使用shift+F9可以查看变脸给的值,包括地址;
6、cout刷新缓冲区的情况?
刷新缓冲区的意思就是往终端上打印;
① 缓冲区满了;
② 程序执行到main的return语句;
③ 调用std::endl强制刷新缓冲区;
④ 系统不繁忙的时候,也会查看缓冲区内容,正常输出到终端;
7、范围for语句的使用技巧?
首先看一下正常的使用:
int x[]{1, 2, 3, 4};
for (auto v:x)
{
cout << v <<endl;
}
其实上述代码有一个可优化的点,上述中其实执行了拷贝构造,将x的值赋值给了v,可修改为:
for (auto &v:x)
{
cout << v << endl;
}
采用引用别名的形式,去除拷贝的过程,提高系统效率;
8、C++内存分布?
可以简单理解成五个区域:
1、栈:函数内部的局部变量放置的区域,由编译器自动分配和释放;
2、堆:程序员malloc、new分配,再用free、delete释放;(忘记释放,程序结束后系统回收)
3、全局/静态存储区:全局变量和静态变量static,程序结束时系统释放;
4、常量存储区;
5、程序代码区;
9、malloc的使用?
首先需要说明一点,malloc是c中遗留下来的,在C++中建议使用new和delete替代malloc和free;
函数原型:
void *malloc(int NumBytes); // NumBytes表示要分配的字节数
返回值:成功则返回指向被分配内存的指针,失败返回NULL;
使用的话也需要定义一个指针来接收:
// 分配int*的内存
int *p = NULL;
p = (int *)malloc(sizeof(int));
if (p != NULL)
{
*p = 5;
}
// 分配char*的内存
char *p = NULL;
p = (char *)malloc(100 * sizeof(char));
if (p != NULL)
{
strcpy_s(p, 10, "hello"); // 中间的数代表分配的大小,小于传入字符串大小则会报错
}
下面看一个简单指针的使用:
int *p = (int *)malloc(sizeof(int) * 100); // 分配100个整形空间大小的内存空间
if (p != NULL)
{
int *q = p; // 目前的p值表示内存的首地址
*q++ = 1; // 由于++和*优先级相同(右结合),这里理解成*(q++),也就是q赋值为1,然后向后移动一个整数字节位数
*q++ = 2;
cout << *p << endl; // 1
cout << *(p+1) << endl; // 2
}
需要对这段代码足够的理解,C++的底层都是指针操作,包括引用也是指针的操作;
10、new和delete的使用?
首先说明一下和malloc的区别,相比malloc和free做了更多的初始化工作;
一般使用形式有如下三种:
① 指针变量名 = new 类型标识符;
② 指针变量名 = new 类型标识符(初始值);
③ 指针变量名 = new 类型标识符[内存单元个数];
// 方法一
int *p = new int;
if (p != NULL)
{
*p = 16;
cout << *p << endl;
delete p;
}
// 方法二
int *p = new int(16);
// 方法三
int *p = new int[100]; // 开辟了大小为100的整形数组空间
if (p != NULL)
{
int *q = p;
*q++ = 1;
*q++ = 2;
delete[] p; // 注意释放的时候也需要加入[]
}
11、NULL和nullptr二者有区别吗?
实际上nullptr是用来防止整型和指针类型的混乱,但二者通过if判断是相等的;
int *p = NULL;
int *q = nullptr;
if(p == q){} // 二者比较是相等的
// 打印二者的类型又是不等的
cout << typeid(NULL).name() << endl; // int
cout << typeid(nullptr).name() << endl; // std::nullptr_t
12、private主要用处?
作用:从实际代码来看,一般用来将函数变量设置为私有的,这些变量仅供内部成员函数访问;
13、C++中类和结构有什么区别?
首先要说明,在C++中最好使用类,毕竟是C++后引入的;
不同点在于:类中成员函数和成员变量默认访问级别是private,结构中默认访问级别是pubilc;
14、后置返回类型函数?
在刷Leetcode题目时往往会看到返回类型在参数列表之后,实际上这种写法是可以的,在此记录一下:
auto fun(int a) -> void;
只需要理解即可,一般不是使用;
15、内联函数的作用?
在函数前加关键字inline,使该函数变为内联函数;
解决问题:函数体小,调用却很频繁的这种函数(频繁调用会压栈出栈,消耗内存且效率低);
作用:
① inline可以影响编译器,在编译阶段对inline这种函数进行处理,尝试将调用函数的动作替换为函数本体;
② inline只是开发者对编译器的建议,是否替换由编译器决定(决定权在编译器)
③ 内联函数的定义(本体)就需要写在头文件中;(include才能把源代码尝试替换进来)
④ constexpr函数可以看作是一个严格的内联函数;
缺点:可能带来代码膨胀的问题,所以函数体尽量小;
16、函数的一些杂合用法?
① 函数返回类型为void,表示函数不返回任何类型;可以返回一个返回类型是void的函数;
void funca(){}
void funcb()
{
return funca();
}
② 函数返回值为指针
int *func()
{
int tmp = 16;
return &tmp;
}
上面函数返回一个局部变量的地址是不正确的,在调用完函数后,局部变量已经被系统回收,此时返回的地址不是可操控的地址,可能引发问题;
③ 函数没有形参可以保持形参列表为空(),或者为(void);
④ 如果一个函数我们不调用的话,则函数可以只有声明没有定义;
⑤ 普通函数,定义只能定义一次(放在.cpp文件中),声明可以声明多次;
⑥ 在C++的函数参数中,建议多使用引用的形式,能减少拷贝构造,提高效率;
17、初始化时const和指针组合的一些区别?
思考一下一下三种写法有什么区别?
char str[] = "hello";
# 写法一
const char* p = str;
# 写法二
char const *p = str;
# 写法三
char * const p = str;
p++; // 错误的写法
上面三种写法中,第一第二种写法作用是相同的,都是不能修改p指向目标的内容,但可以修改p的指向;
第三种写法是指针的指向不能改变,指针指向的目标的内容可以修改;
18、函数形参中带const的作用?
作用:不允许修改形参的值,一般能加的都要加上;
好处:
① 可以防止无意间修改形参的值导致实参被修改;
② 实参类型更加灵活;(主要是支持的类型更多了)
19、string的几种初始化方式?
string作为C++标准库的类型,是我们最常用的一种类类型之一;
初始化方式有以下几种:
#include<string>
using namespace std;
string s; // 默认初始化, s = ""空串,表示没有字符;
string s1 = "Hello World!"; // 把字符串拷贝到s1这一段内存中,不包括末尾的\0;
string s2("Hello World"); // 与s1的效果一致;
string s3 = s1; // 把s1中的内容拷贝到s3代表的一段内存中;
int num = 10;
string s4(num, 's'); // ssssssssss,10个s组成的字符串,并不常用;
20、string的常见方法
主要有以下一些常见方法:
string s1;
// 判空
s1.empty()
// 返回字节/字符数量,代表字符串的长度
s1.size(); s1.length();
// 索引,类似数组
s1[n];
// 返回字符串s1中的内容指针,以\0结尾,实际上是为了兼容C语言,将string对象转为C语言中的样式
const char *p = s1.c_str();
char s1[] = "hello"; // 这是C语言遗留下来的字符数组定义
// 范围for处理string对象
for(auto &a : s1)
{
a = 'i'; // 将所有字符改为i,直接改变传入数据
}