由于本人之前有C基础,C++的程度是基本语法+STL+ C with class的程度,对类与对象和高级特性没有学习, 因此学习中只记录自己不知道的,其他已知的掠过。
教程来自:https://www.w3cschool.cn/cpp/
最新的ISO C++标准是C++20。请注意,C++标准在不断更新,可能会有新的版本发布。建议查阅ISO的官方网站或C++标准委员会的官方资料以获取最新信息。也就是C++后面的数字,目前更新到了C++20——第六个C++标准。
我用的是CLion + single execution。
#include
using namespace std;
// main() 是程序开始执行的地方
int main(){
cout << "Hello World"; // 输出 Hello World
return 0;
}
asm | else | new | this |
---|---|---|---|
auto | enum | operator | throw |
bool | explicit | private | true |
break | export | protected | try |
case | extern | public | typedef |
catch | false | register | typeid |
char | float | reinterpret_cast | typename |
class | for | return | union |
const | friend | short | unsigned |
const_cast | goto | signed | using |
continue | if | sizeof | virtual |
default | inline | static | void |
delete | int | static_cast | volatile |
do | long | struct | wchar_t |
double | mutable | switch | while |
dynamic_cast | namespace | template |
在计算机编程中,“三字符组”(trigraphs)是一种特殊的字符序列,由两个问号(??)开头,后跟一个表示特定字符的符号。它们主要用于早期的计算机系统,其中可能没有直接输入某些特殊字符的键盘按键。通过使用三字符组,程序员可以在这些系统上输入不容易键入的字符。
例如,"??=“表示#,”??(“表示[,”??/"表示\,等等。但是,现代编程环境中,通常不再使用三字符组,因为现代的键盘和编程工具已经可以方便地输入所有字符。
(C++中不常用,了解即可 )
另一方面,在下面的语句中:
fruit = apples + oranges; // 获取水果的总数
fruit 和 =,或者 = 和 apples 之间的空格字符不是必需的,但是为了增强代码的可读性 ,您可以根据需要适当增加一些空格。
在 /* 和 / 注释内部,// 字符没有特殊的含义。在 // 注释内,/ 和 */ 字符也没有特殊的含义。因此,您可以在一种注释内嵌套另一种注释。例如:
/* 用于输出 Hello World 的注释
cout << "Hello World"; // 输出 Hello World */
wchar_t
是 C++ 中的一种数据类型,用于表示宽字符(wide characters)。在不同的编译环境下,wchar_t
的大小可能会不同,但它通常被设计成足够大,可以容纳任何可以用单个字符表示的 Unicode 字符。
在 C++ 中,wchar_t
主要用于处理多语言环境下的宽字符文本。宽字符文本通常用于需要支持非拉丁字符集(如中文、日文、俄文等)的应用程序中。
您可以使用 wchar_t
类型来声明宽字符变量,例如:
wchar_t myWideChar = L'你'; // 宽字符 '你',前面的 L 表示它是一个宽字符
下面实例会输出您电脑上各种数据类型的大小。
#include
using namespace std;
int main()
{
cout << "Size of char : " << sizeof(char) << endl;
cout << "Size of int : " << sizeof(int) << endl;
cout << "Size of short int : " << sizeof(short int) << endl;
cout << "Size of long int : " << sizeof(long int) << endl;
cout << "Size of float : " << sizeof(float) << endl;
cout << "Size of double : " << sizeof(double) << endl;
cout << "Size of wchar_t : " << sizeof(wchar_t) << endl;
return 0;
}
(面试经常会出sizeof的题 )
例如,下面的代码定义了一个颜色枚举,变量 c 的类型为 color。最后,c 被赋值为 “blue”。
enum color { red, green, blue } c;
c = blue;
默认情况下,第一个名称的值为 0,第二个名称的值为 1,第三个名称的值为 2,以此类推。但是,您也可以给名称赋予一个特殊的值,只需要添加一个初始值即可。例如,在下面的枚举中,green 的值为 5。
enum color { red, green=5, blue };
在这里,blue 的值为 6,因为默认情况下,每个名称都会比它前面一个名称大 1。
同样的,在函数声明时,提供一个函数名,而函数的实际定义则可以在任何地方进行。例如:
// 函数声明
int func();
int main()
{
// 函数调用
int i = func();
}
// 函数定义
int func()
{
return 0;
}
作用域是程序的一个区域,一般来说有三个地方可以声明变量:
在程序中,局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值。
当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动初始化为下列值:
数据类型 | 初始化默认值 |
---|---|
int | 0 |
char | ‘\0’ |
float | 0 |
double | 0 |
pointer | NULL |
(正确地初始化变量是一个良好的编程习惯,否则有时候程序可能会产生意想不到的结果 )
整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。
整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。
浮点常量由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。
下面列举几个浮点常量的实例:
`3.14159 // 合法的
314159E-5L // 合法的
510E // 非法的:不完整的指数
210f // 非法的:没有小数或指数
.e55 // 非法的:缺少整数或分数`
字符常量是括在单引号中。如果常量以 L(仅当大写时)开头,则表示它是一个宽字符常量(例如 L’x’),此时它必须存储在 wchar_t 类型的变量中。否则,它就是一个窄字符常量(例如 ‘x’),此时它可以存储在 char 类型的简单变量中。
字符常量可以是一个普通的字符(例如 ‘x’)、一个转义序列(例如 ‘\t’),或一个通用的字符(例如 ‘\u02C0’)。
下表列出了一些这样的转义序列码:
转义序列 | 含义 |
---|---|
\ | \ 字符 |
’ | ’ 字符 |
" | " 字符 |
? | ? 字符 |
\a | 警报铃声 |
\b | 退格键 |
\f | 换页符 |
\n | 换行符 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ooo | 一到三位的八进制数 |
\xhh . . . | 一个或多个数字的十六进制数 |
您可以使用 const 前缀声明指定类型的常量,如下所示:
const type variable = value;
(这里之后和int const有区别 )
(请注意,把常量定义为大写字母形式,是一个很好的编程实践。 )
修饰符就是
C++ 允许使用速记符号来声明无符号短整数或无符号长整数。您可以不写 int,只写单词 unsigned short 或 unsigned long,int 是隐含的:
unsigned x;
unsigned int y;
限定符 | 含义 |
---|---|
const | const 类型的对象在程序执行期间不能被修改改变。 |
volatile | 修饰符 volatile 告诉编译器,变量的值可能以程序未明确指定的方式被改变。 |
restrict | 由 restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。 |
在 C++ 中,volatile
是一个关键字,用于告诉编译器,被 volatile
修饰的变量随时可能会被意外地改变,因此编译器不应该对这个变量进行优化。这通常用在多线程环境中或者在与硬件进行交互的程序中。
该变量的值可能会被程序以外的因素改变,因此编译器在生成代码时应该避免对这个变量的操作进行优化。这就防止了编译器将变量的值缓存到寄存器或者对变量进行类似的优化,因为这些操作可能会导致与外部改变的值不一致的情况。
restrict
关键字用于向编译器传达指针之间的独占关系,即指针所指向的内存区域不会被其他指针访问或修改。使用 restrict
关键字可以让编译器进行更有效的优化,例如寄存器的使用和循环优化。
存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。下面列出 C++ 程序中可用的存储类:
其中auto比较特殊:在C++11 中, auto 关键字不再是C++存储类说明符。从C++11开始,auto 关键字声明一个变量,该变量的类型是从其声明中的初始化表达式推导出来的。
auto原本表示所有变量的默认类型,只能修饰局部变量。
register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 ‘&’ 运算符(因为它没有内存位置)。
{
register int miles;
}
使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。
static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。
extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 ‘extern’ 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。
当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。也就是说:extern它用于指示编译器一个标识符是在其他文件或编译单元中定义的,而不是当前文件中定义的。
外部变量声明:当你在一个文件中声明一个全局变量,并且想在另一个文件中使用该变量时,你需要在使用的文件中使用 extern
关键字进行声明。这告诉编译器该变量是在其他文件中定义的,并且可以在当前文件中使用。例如:
// File1.cpp
int globalVar; // 定义全局变量
// File2.cpp
extern int globalVar; // 声明全局变量,指示它在其他文件中定义
// 可以在此处使用 globalVar 变量
外部函数声明:当你在一个文件中定义一个函数,并且想在另一个文件中调用该函数时,你需要在调用的文件中使用 extern
关键字进行声明。这样编译器就知道该函数是在其他文件中定义的,并且可以在当前文件中调用。例如:
// File1.cpp
void someFunction() {
// 函数定义
}
// File2.cpp
extern void someFunction(); // 函数声明,指示它在其他文件中定义
// 可以在此处调用 someFunction 函数
运算符 | 描述 |
---|---|
sizeof | sizeof 运算符返回变量的大小。例如,sizeof(a) 将返回 4,其中 a 是整数。 |
Condition ? X : Y | 条件运算符。如果 Condition 为真 ? 则值为 X : 否则值为 Y。 |
, | 逗号运算符会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。 |
.(点)和 ->(箭头) | 成员运算符用于引用类、结构和共用体的成员。 |
Cast | 强制转换运算符把一种数据类型转换为另一种数据类型。例如,int(2.2000) 将返回 2。 |
&(这里不是按位与的意思) | 指针运算符 & 返回变量的地址。例如 &a; 将给出变量的实际地址。 |
*(解引用) | 指针运算符 * 指向一个变量。例如,*var; 将指向变量 var。 |
一般情况下,C++ 程序员偏向于使用 for(; 结构来表示一个无限循环。
return_type function_name( parameter list )
{
body of the function
}
函数声明包括以下几个部分:
return_type function_name( parameter list );
在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明:
int max(int, int);
当调用函数时,有三种向函数传递参数的方式:
调用类型 | 描述 |
---|---|
传值调用(默认) | 该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。 |
指针调用(void swap(int *x, int *y)) | 该方法把参数的地址复制给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。 |
引用调用(void swap(int &x, int &y) | 该方法把参数的引用复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。 |
cpp是支持默认赋值的:
int sum(int a, int b=20){
int result;
result = a + b;
return (result);
}
C++ 内置了丰富的数学函数,需要引用数学头文件
序号 | 函数 & 描述 |
---|---|
1 | double cos(double); 该函数返回弧度角(double 型)的余弦。 |
2 | double sin(double); 该函数返回弧度角(double 型)的正弦。 |
3 | double tan(double); 该函数返回弧度角(double 型)的正切。 |
4 | double log(double); 该函数返回参数的自然对数。 |
5 | double pow(double, double); 假设第一个参数为 x,第二个参数为 y,则该函数返回 x 的 y 次方。 |
6 | double hypot(double, double); 该函数返回两个参数的平方总和的平方根,也就是说,参数为一个直角三角形的两个直角边,函数会返回斜边的长度。 |
7 | double sqrt(double); 该函数返回参数的平方根。 |
8 | int abs(int); 该函数返回整数的绝对值。 |
9 | double fabs(double); 该函数返回任意一个十进制数的绝对值。 |
10 | double floor(double); 该函数返回一个小于或等于传入参数的最大整数。 |
有两个相关的函数。一个是 rand(),该函数只返回一个伪随机数。生成随机数之前必须先调用 srand() 函数(产生种子)
#include
#include
#include
using namespace std;
int main ()
{
int i,j;
// 设置种子
srand( (unsigned)time( NULL ) );
/* 生成 10 个随机数 */
for( i = 0; i < 10; i++ ){
// 生成实际的随机数
j= rand();
cout <<"随机数: " << j << endl;
}
return 0;
}
声明跟C一样。
C++ 不允许向函数传递一个完整的数组作为参数,但是,您可以通过指定不带索引的数组名来传递一个指向数组的指针。也不允许返回一个完整的数组作为函数的参数。但是,您可以通过指定不带索引的数组名来返回一个指向数组的指针。参与处理的都是数组的首地址 )
另外,C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。
C++ 提供了以下两种类型的字符串表示形式:
C++ 中有大量的函数用来操作以 null 结尾的字符串:
序号 | 函数 & 目的 |
---|---|
1 | strcpy(s1, s2); 复制字符串 s2 到字符串 s1。 |
2 | strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。 |
3 | strlen(s1); 返回字符串 s1 的长度。 |
4 | strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1 |
5 | strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 |
6 | strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
#include
#include
using namespace std;
double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
double& setValues( int i )
{
return vals[i]; // 返回第 i 个元素的引用
}
// 要调用上面定义函数的主函数
int main ()
{
cout << "改变前的值" << endl;
for ( int i = 0; i < 5; i++ )
{
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
setValues(1) = 20.23; // 改变第 2 个元素
setValues(3) = 70.8; // 改变第 4 个元素
cout << "改变后的值" << endl;
for ( int i = 0; i < 5; i++ )
{
cout << "vals[" << i << "] = ";
cout << vals[i] << endl;
}
return 0;
}
这里如果去掉setValues前面的&,代码会报错,因为此时的setValues(1)不能作为左值了。
C++ 标准库没有提供所谓的日期类型。C++ 继承了 C 语言用于日期和时间操作的结构和函数。为了使用日期和时间相关的函数和结构,需要在 C++ 程序中引用头文件。
以下是一个获取当前时间的例子:
#include
#include
using namespace std;
int main( )
{
// 基于当前系统的当前日期/时间
time_t now = time(0);
// 把 now 转换为字符串形式
char* dt = ctime(&now);
cout << "本地日期和时间:" << dt << endl;
// 把 now 转换为 tm 结构
tm *gmtm = gmtime(&now);
dt = asctime(gmtm);
cout << "UTC 日期和时间:"<< dt << endl;
}
当将 0
作为参数传递给 time()
函数时,它告诉函数使用默认的起点时间来计算当前时间。这意味着 time(0)
返回的值是从起点时间到当前时间的秒数。
还有如下函数:
序号 | 函数 & 描述 |
---|---|
1 | *time_t time(time_t time); 该函数返回系统的当前日历时间,自 1970 年 1 月 1 日以来经过的秒数。如果系统没有时间,则返回 .1。 |
2 | **char ctime(const time_t time); 该返回一个表示当地时间的字符串指针,字符串形式 day month year hours:minutes:seconds year\n。 |
3 | **struct tm localtime(const time_t time); 该函数返回一个指向表示本地时间的 tm 结构的指针。 |
4 | clock_t clock(void); 该函数返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。如果时间不可用,则返回 .1。 |
5 | char * asctime ( const struct tm * time ); 该函数返回一个指向字符串的指针,字符串包含了 time 所指向结构中存储的信息,返回形式为:day month date hours:minutes:seconds year\n\0。 |
6 | **struct tm gmtime(const time_t time); 该函数返回一个指向 time 的指针,time 为 tm 结构,用协调世界时(UTC)也被称为格林尼治标准时间(GMT)表示。 |
7 | *time_t mktime(struct tm time); 该函数返回日历时间,相当于 time 所指向结构中存储的时间。 |
8 | double difftime ( time_t time2, time_t time1 ); 该函数返回 time1 和 time2 之间相差的秒数。 |
9 | size_t strftime(); 该函数可用于格式化日期和时间为指定的格式。 |
C++ 的 I/O 发生在流中,流是字节序列。如果字节流是从设备(如键盘、磁盘驱动器、网络连接等)流向内存,这叫做输入操作。如果字节流是从内存流向设备(如显示屏、打印机、磁盘驱动器、网络连接等),这叫做输出操作。
下列的头文件在 C++ 编程中很重要。
头文件 | 函数和描述 |
---|---|
该文件定义了 cin、cout、cerr 和 clog 对象,分别对应于标准输入流、标准输出流、非缓冲标准错误流和缓冲标准错误流。 | |
该文件通过所谓的参数化的流操纵器(比如 setw 和 setprecision),来声明对执行标准化 I/O 有用的服务。 | |
该文件为用户控制的文件处理声明服务。我们将在文件和流的相关章节讨论它的细节。 |
为了定义结构,您必须使用 struct 语句。定义方法和C一样:
struct Books{
char title[50];
char author[50];
char subject[100];
int book_id;
}book;
注意.和->调用成员的区别
typedef struct
{
char title[50];
char author[50];
char subject[100];
int book_id;
}Books;