【笔记】C++语言学习笔记

目录

语法规范

声明定义与作用域

指针专题

类型转换

常用函数方法

输入输出

STL使用相关

C++11新特性

语法规范

  1. C++中主程序要写成
    1
    2
    3
    int main(){
    return 0;
    }
    不能写成void main,这是不规范的有些g++编译器会报错,return 0表示已经执行完成了

且C++规定:不明确标明返回值的,默认返回值为int,也就是说main()等同于int main(),但会被警告

2.数组元素个数一般用nums.size(),使用sizeof(nums)/sizeof(int)的形式在某些时刻不保险

3.队列的两种实现形式

  • 使用循环数组
  • 使用链表

4.栈stack:push(), top(), pop(), empty(), size()

队列queue:push(), front(), pop(), empty(), size()

5.标准库的头文件用<>,非标准库的头文件用””(一般是.h文件)

声明定义与作用域

1.static关键字的作用

  • 全局静态变量(在全局变量前加static):作用域为声明语句所在的整个文件
  • 局部静态变量(在局部变量前加static):作用域仍为局部

(静态变量都放在内存的静态存储区内,在程序运行期间一直存在不会释放,所有在函数内定义的局部静态变量,会在第一次运行到该语句时就创建,第二次运行到该定义语句会自动跳过,直到程序终止才释放)

  • 静态函数(在函数返回类型前加static):作用域为声明语句所在的整个文件
  • 类的静态成员和静态函数:这个类的所有对象都有,注意静态函数只能引用类中的静态成员(静态成员:类和子类的所有方法都可以访问)

总结:static与extern相反,static意味着该变量、函数在被声明的文件之外是不可见的,除非引入声明的文件作为头文件,但是这样的话会每一个引入该头文件的文件都会定义一个自己的变量造成内存浪费,所以一般用extern在头文件中声明才是正确的方式

2.全局声明,局部定义vector实例

1
2
3
4
5
6
7
8
9
10
11
vector<int> temple;
int main(int k){
temple = vector<int> (k);
box;
......
}
void box(){
int n = temple.size();
}

# 此时n为2

指针专题

1.指针和引用的区别

  • 指针有自己的一块空间,用sizeof看一个指针大小是4;引用知识一个别名不占空间,用sizeof看引用大小是被引用对象的大小
  • 指针可初始化为NULL,引用则必须初始化为一个已有对象
  • 作为参数传递时,指针必须被解引用才可以对对象修改,而引用的修改会直接改变被引对象(因此有const指针,却没有const对象)
  • 指针可以有多级(**p),引用却只有一级
  • 指针和引用使用++运算符的意义不一样
  • 返回动态分配的对象、内存必须用指针,因为引用可能引起内存泄漏

2.指针值为NULL和nullptr的区别

  • NULL是一个宏定义,在c和c++中的定义不同,c中NULL为(void*)0,而c++中NULL为整数0。所以在c++中int *p=NULL;实际表示将指针P的值赋为0,而c++中当一个指针的值为0时,认为指针为空指针
  • nullptr是一个字面值常量,类型为std::nullptr_t,空指针常数可以转换为任意类型的指针类型。在c++中(void *)不能转化为任意类型的指针,即 int p=(void)是错误的,但int *p=nullptr是正确的

3.智能指针:实质上是一个类,主要用于管理在堆上分配的内存,将普通的指针封装为一个栈对象。当栈对象生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏

  • auto_ptr:采用所有权模式
  • unique_ptr:实现独占式拥有,保证同一时间内只有一个智能指针可以指向该对象
  • shared_ptr:实现共享式拥有,使用计数机制来表面资源被几个指针共享,每多一个计数加1,计数等于0时资源释放(两个共享指针互相指向对方会造成循环引用,计数失效而导致内存泄漏)
  • weak_ptr:用于解决上面循环引用导致的内存泄漏,它不会增加对象的引用计数,可以与shared指针环形转化(注意:不能通过weak指针直接访问对象的方法,要先将其转化成shared指针再访问)

4.野指针:指向一个已删除对象或未申请访问受限内存区域的指针

类型转换

1.(struct 类型名*)&变量名表示把该变量强转成该类型

1
2
3
4
# serv_addr是sockaddr_in类型的变量
struct sockaddr_in serv_addr;
# 第二个参数传进去的实际是sockaddr类型
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))

2.C++中的四种类型转换(区别于强转是为了便于追踪错误)

  • const_cast:将const变量转为非const,使其能修改,常用于函数重载
  • static_cast:用于各自隐式转换,如非const转const、void*转指针等,也适用于大数转小数,告知编译器不在乎精度损失
  • dynamic_cast:用于动态类型转换(转指针、引用),只能用于含有虚函数的类,用于类层次间的向上和向下转化
  • reinterpret_cast:几乎什么都可以转,比如int转指针,但有可能会出问题

常用函数方法

1.memset()用于填充字符串,原型是void *memset(void *str, int c, size_t n)

1
2
3
4
5
6
7
char str[50];

strcpy(str,"This is string.h library function");
# str = "This is string.h library function"

memset(&str,'$',7);
# str = "$$$$$$$ string.h library function"

2.在vector中使用find(num.begin(), num.end(), 元素值)

  • 找到,返回下标
  • 找不到,返回num.end()

3.在字符串中使用find函数

1
2
3
4
str="ABCDE";
int idx = str.find('E', 0)
# 0表示从str下标为0的位置往后开始查找
# idx = 4

4.str1.substr(x,n)用于从字符串str1下标x开始截取n个字符串

1
2
3
4
str1 = "123456";
str2 = str1.substr(1,3);

# str2 = "234"

输入输出

1.对已经封装好的exe执行程序使用文件作为cin输入,将cout输出到另一个文件

1
2
3
4
# 控制台执行
addItem <infile >outfile

# 表示用当前目录的infile文件作为addItem程序(windows要加.exe)的输入,并把输出保存到当前目录的outfile文件

STL使用相关

1.vector的截取数组

1
2
3
4
5
6
vector<int> nums {1,2,3,4,5,6,7,8,9};
vector<int>::const_iterator First = nums.begin() + 1;
vector<int>::const_iterator Second = nums.begin() + 3;
vector<int> nums2(First, Second);

# 此时nums2等于{2,3,4},即[nums[First],Nnums[Second]],注意两边都是闭区间

2.vector的数组合并

1
2
3
4
5
6
7
8
9
10
11
12
vector<int> nums0 = {1,2,3};
vector<int> nums1 = {4,5,6,7};
vector<int> nums2 = {8,9};

# 将nums1数组加到nums0数组末尾
nums0.insert(nums0.end(),nums1.begin(),nums1.end());

# 将nums0数组加到nums2数组前面
nums2.insert(nums2.begin(),nums0.begin(),nums0.end());

# nums0 = {1,2,3,4,5,6,7}
# nums2 = {4,5,6,7,8,9}

3.二维vector的初始化

1
2
3
4
5
6
7
8
9
10
# 第一种
vector<vector<int>> nums(n,vector(m));
# 第二种
vector<vector<int>> nums;
for(int i = 0; i < m ; i++){
nums.push_back(vector<int>());
for(int j = 0; j < n; j++){
nums[i].push_back(0);
}
}

4.使用以下形式赋vector初值

1
vector<int> hash = vector<int> (1000, 1);

可以避免产生歧义而编译不通过

5.哈希表特殊用法

1
unordered_map <TreeNode*, int> f;

则有f[root]是一个int值,root是一个树节点指针

6.数组用==、!=会比较首内存地址(即是不是指向同一内存),而vector数组就可以用==、!=比较数组中包含的元素是否一样

7.sort()自定排序规则

1
2
3
4
5
sort(nums.begin(), nums.end(), [](xx a, xxb){
语句规则
});
# xx是a、b的类型名
# 语句规则return true时表示不变,return false表示会交换a、b的位置

8.优先队列priority_queue的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
//定义
priority_queue<int> a;

//会从a中弹出值最大的元素
int temple = a.top();
a.pop()

//常规操作
a.empty();
a.push();//也写作a.emplace();
a.size();

# 定义相当于priority_queue<int, vector<int>, less<int> > a;若将less改为greater就是小根堆了

编译相关

1.GCC中有gcc(c编译器)和g++(c++编译器)

  • 后缀为.c的文件,gcc当成c文件处理,g++当成c++处理
  • 在编译阶段,g++会自动链接STL库,而gcc不会
  • gcc在编译c文件时,可用的预定义宏是比较少的

C++11新特性

1.数据类型long long是在C++11中新定义的,规定一个long long至少和一个long一样大