个人有整理笔记的习惯,把一些C++学习笔记分享给大家,供大家参考学习💖
内容放在一篇文章中还是有些耗费我的时间所以我决定分为八个板块,这是第一个板块
废话不多说,上目录!
目录
从C到C++
Part0:C++简介
面向过程:C语言
特点:
- 关注重点为算法;(怎么解决问题)
- "算法"可以认为是有序的步骤,按照此步骤可以达到预期效果;
- 面向过程的程序通常为有序的语句,接近计算机执行命令的本质;
- 开发效率低,但代码执行率高。
面向对象:C++
特点:
- 关注重点为对象(谁来解决问题)
- “对象"本质是一系列由于某种联系聚焦在一起的数据,程序员需要处理这些"对象之间的关系”
- 接近人类看待事物的方式
- 开发效率高,但是代码执行率低
Part1:引用reference
1.1 引用的概念
概念:引用与指针非常相似,但是弱化了程序员对内存的控制;
引用符:
&:表示一个变量的"别名",对引用进行操作与直接操作变量完全一样。
例:int a=100; int& b = a; // a和b完全相同,包括地址;
1.2 引用的性质
可以改变引用的变量的值,但是不能再次成为其它变量的引用
int a = 100;
int& b = a;// b是a的引用
int c = 200;
b = c; // b不会成为c的引用,只是完成了赋值
// &b = c; 错误
// int& b = c; 错误
声明引用时,必须同时对其进行初始化,初始化的值不能为NULL
int a = 100;
// int& b; 错误
// int& b = NULL; 错误
int& b = a;
声明引用时,初始化的值可以是纯数值,但是此时需要使用const关键字修饰引用,表示引用的值不可变,也称为常引用
// int& b = 200; 错误
const int& b = 200;// b是常引用
// b++; 错误
可以将变量引用的地址赋值给一个指针,此时指针指向的还是原来的变量
int a = 10;
int& b = a;
int* c = &b; // c指向b,b是a的引用,相当于c指向了a
(*c)++;
cout << *c << " " << a << " " << b << endl; // 10 10 10
可以建立指针的引用
int a = 10;
int* b = &a; // b指向了a
int*& c = b; // c是b的引用
cout << c << " " << b << endl; // 0x61fe88 0x61fe88
cout << *c << " " << *b << " " << a << endl; // 10 10 10
可以用const修饰引用,此时不允许改变引用的值,但是可以改变原变量的值
int a = 10;
const int& b = a; // b是a的常引用
// b++; 错误
a++;
cout << a << " " << b << endl; // 11 11
1.3 引用参数
使用引用参数实现swap函数
#include <iostream>
using namespace std;
/**
* @brief swap1 错误,因为传递参数时会生成局部副本
*/
void swap1(int a,int b)
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
cout << a << endl; // 2
cout << b << endl; // 1
}
/**
* @brief swap2 正确但繁琐
*/
void swap2(int* a,int* b)
{
*a = *a ^ *b;
*b = *a ^ *b;
*a = *a ^ *b;
}
/**
* @brief swap3 正确且简洁
*/
void swap3(int& a,int& b)
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
概念:
使用引用做为参数可以减少参数传递的开销,因为在参数传递的过程中不产生副本,可以使参数传递的效率提高
注意:
引用参数应该在能被定义为const的情况下,尽量定义为const,这样可以达到引用的安全性
函数传递分为三种:
值传递、地址传递、引用
名称 | 特点 |
---|---|
值传递 | 将变量的值赋给形式参数,无法改变原地址的值,形参会开辟空间来存放数据 |
地址传递 | 将变量的地址传给形参,可以对原变量的值进行更改,形参会开辟空间来存放数据 |
引用 | 直接在原变量的地址上直接操作,可以更改变量的值,不需要开辟空间存放数据 |
Part2:赋值
2.1 概念
C++中为赋值指定了一些新的写法,以便于与后期面向对象的写法对应
传统编程中使用赋值运算符=来进行赋值操作,C++中也支持使用括号来赋值
#include <iostream>
using namespace std;
int main()
{
// 使用赋值运算符
int a = 1;
int b = a;
cout << b << endl; // 1
// C++可以如下操作,效果等同上述
int c(1);
int d(c);
cout << &c << " " << &d << endl; // 0x61fe84 0x61fe80
cout << d << endl; // 1
return 0;
}
2.2 数据窄化
数据丢失——数据窄化
C++11
中引入{}
赋值方式,会对数据窄化警告
#include <iostream>
using namespace std;
int main()
{
double a = 3.14;
int b = a; // 3
int c(a); // 3
int d{
a}; // 3------C++11中引入{}赋值符号,会对数据窄化警告
cout << b << " " << c << " " << d << endl; // 3 3 3
return 0;
}
Part3:键盘输入
C++中
使用cout
把程序中的内容输出到命令行
使用cin
获取键盘输入,QT中有更好的解决方案
支持连续输入输出
连续输入可以使用空格一次性输入并间隔,也可以分多次输入
Part4:初识字符串 string
4.1 string
类基本使用
概念:
C++中使用string
来表示标准字符串,有别于基本数据类型(如int
等)
string
实际上是一个C++自带的类
使用时需要引入头文件#include <string>
而不是<string.h>
string
的设计是符合面向对象的,内置大量字符串处理的相关函数
使用相比char*
要更加简便,例如无需担心内存是否足够、字符串长度等
使用注意:
s.size()
和s.length()
的返回值是个unsigned int
数据
如需使用返回值,需定义一个unsigned int
来接收(针对warning
)
遍历:
string s = "Hello";
// 取出单个字符
char c1 = s[0];
cout << c1 << endl; // H
char c2 = s.at(0);
cout << c2 << endl; // H
// 获取字符串长度,下面两种写法等同
cout << s.size() << endl; // 5
cout << s.length() << endl; // 5
// for循环遍历
for(int i = 0;i < s.size();i++)
{
cout << s[i] << " ";
}
cout << endl;
// C++11新增for-each遍历
for(char i:s)
{
cout << i << " ";
}
cout << endl;
// 迭代器遍历后面讲解
4.2 取出单个元素
函数名称 | 使用时的区别 | 特点 |
---|---|---|
[] |
当下标越界时,使用[] 取出元素会直接返回一个\0 程序正常执行 |
取出的效率更高,但安全性较差,且不会知道问题具体出在哪里 |
at |
程序运行到错误位置会终止,并输出错误信息 | 凸出越界问题,安全性更高,及时发现及时处理,防止出现更大的Bug |
建议使用at
函数,除非可以确保取出的下标一定正确
Part5:函数
5.1 内联函数
C++引入内联函数取代C语言中宏定义的函数,可以解决程序中函数调用的效率问题
内联函数在编译时将函数体中的内容展开到主函数中进行内容替换
牺牲编译的速度换取执行的速度,执行效率更高
#include <iostream>
using namespace std;
void show_long(string s1,string s2); // 先声明
int main()
{
show_long("aaaaa","bbb");
return 0;
}
inline void show_long(string s1,string s2) // 再定义
{
s1.size() > s2.size() ? cout << s1 : cout << s2;
}
使用内联函数的要求:
- 内联函数的代码长度限制在1-5行,且不能包含复杂的循环或流程控制语句
- 内联函数最好是频繁使用的函数
- 使用关键字
inline
放在函数定义的前面 - 成员函数默认为内联函数
5.2 函数重载overload
C++允许用同一个函数名称定义多个函数,这种用法就是函数重载
理解:
相同函数名,参数(个数、类型)不一样,做各自的事情,返回值类型与函数重载无关!
无demo
说丁8
上demo
#include <iostream>
using namespace std;
void print(string s)
{
cout << "1" << endl;
}
void print(int i)
{
cout << "2" << endl;
}
void print(int i1,int i2)
{
cout << "3" << endl;
}
int main()
{
print(""); // 1
print(1, 1); // 3
print(2); // 2
return 0;
}
对编译器产生误导,导致编译不通过的情况😂
编译器不知道这是long
还是int
😎
#include <iostream>
using namespace std;
void print(long s)
{
cout << "1" << endl;
}
void print(int i)
{
cout << "2" << endl;
}
int main()
{
print(0);
return 0;
}
注意:
构造函数与成员函数也可以重载,但析构函数不能重载❗️
5.3 函数参数的默认(缺省)值
C++中允许给函数的参数设定默认值(缺省值)
调用时如果不传入参数,则参数使用默认值
调用时如果传入参数,传入的参数可以替换默认值
原则:
向右(向后)原则,即函数的某个参数设置默认值之后,其右边(后边)必须设定默认值
注意:
默认值设置可以写在函数声明处,又可以写在函数定义处,建议只写一处
区别:
默认参数无法处理参数类型不同的情况
默认参数无法针对不同个数的传入参数有不同的逻辑响应
使用建议:
建议优先使用默认参数,如果默认参数无法实现目标功能再使用函数重载
非必要不得将函数重载与默认参数同时使用,因为非常容易出现二义性问题ambiguous
代码示例:
#include <iostream>
using namespace std;
void show1(int a,int b=3);
void show1(int a,int b)
{
cout << "1 " << a << b << endl;
}
void show2(int a,int b);
void show2(int a=4,int b=5)
{
cout << "2 " << a << b << endl;
}
void show3(int a,int b=0,int c=8)
{
cout << "3 " << a << b << c << endl;
}
int main()
{