Cpp学习笔记之一
本文最后更新于 2024-10-18,文章内容距离上一次更新已经过去了很久啦,可能已经过时了,请谨慎参考喵。
title: Cpp学习笔记之一
top_img: false
tags:
- C++
categories:
- 编程语言
cover: false
abbrlink: 9edf82f7
date: 2023-12-31 16:11:12
copyright:
comments:
前言
本来是准备一直更新Go语言笔记的了,但是突然发现CAPE-OPEN好像只能用C++语言来写(官方说也可以用VB,但是我觉得吧,VB就算了)
因为CAPE-OPEN应该是要进行COM组件的注册以及操作的,故C++较为适宜,当然Java也是可以的,但是不推荐,理论上支持COM组件以及可以编译为动态链接库的语言都是可以的
所以又得继续拾起C++了,so,开始学习吧骚年,加油!
Hello World
#include <iostream> // 引入标准库
int main() { // 程序开始
std::cout << "Hello World!" << std::endl; // 输出HelloWorld!字符串并换行
return 0; // main函数结束,调用进程返回值0
}
基础语法
现在我们通过解析上述的例子来学习一下C++程序的语法
首先第一行 #include <iostream>
是引入C++的标准库 iostream
,以用来可以输入/出信息,从库名字也可以看出来,I/O
和 stream
就是输入/出信息流嘛;如果要引入一些本地库或第三方库,可以通过使用 #include "[path]"
格式,其中 path
就是头文件/库所在的路径。
第二行 int main() { }
是主函数,程序从这里开始执行,int
是函数类型,main()
是函数名称和传入参数,函数内容放在大括号中。
第三行 std::cout << "Hello World!" << std::endl;
是在屏幕中输出信息,其中 std::
这是个名称空间标示符,是用来声明使用的是标准库中的函数/对象,以防止出现同名的函数/对象产生冲突;
<<
符号在cpp中既可以用来执行位运算,又可以做输入/出流,<<
和 cout
一起使用就是输出,cin
和 >>
一起使用就是输入;
双引号表示字符串就不用说了,后面的 endl
是换行符,这里进行了简略缩写,实际上可以写成如下所示:
std::cout << "Hello World!";
std::cout << std::endl;
// 或者:
std::cout << "Hello World!"; std::cout << std::endl;
在这里可以看到,C++并不是以行末为语句的结束的,而是以 ;
分号为准的。
第四行 return 0;
一般用作表示程序的结束,实际上这个语句根据函数类型的不同而不同,而且 return 0;
在不同的情境下可能含义也是不同的,这个之后再进行深究。
C++标准库(C++ Standard Library),是类库和函数的集合,其使用核心语言写成,由C++标准委员会制定,并不断维护更新。C++强大的功能来源于其丰富的类库及库函数资源。在C++开发中,要尽可能地利用标准库完成,这样可以降低成本,提高编程效率,保证程序质量,又能保持编程风格一致性。C++标准库又分为标准函数库和面向对象类库。
**标准函数库包括:**输入/输出IO、字符串和字符处理、数学、时间、日期和本地化、动态分配、其他、宽字符函数。
**面向对象类库包括:**标准的C++ IO类、String类、数值类、STL容器类、STL算法、STL函数对象、STL迭代器、STL分配器、本地化库、异常处理类、杂项支持库。
哦对,别忘了注释格式哦:
// 这是单行注释,以行末为结束
/* 这是
多行
注释
*/
声明命名空间
在上述例子中,可以看到大量且频繁的使用 std::
命名空间标示符,是比较繁琐的,实际上有一种简单的声明方式可以解决:
#include <iostream>
using namespace std; // 声明命名空间
int main() {
cout << "Hello World!" << endl;
return 0;
}
通过命名空间的声明,可以默认该程序内部使用的都是该库下的函数/对象;也可以只声明需要使用的部分:
#include <iostream>
using std::cout; // 声明命名空间
int main () {
cout << "Hello World!" << std::endl;
return 0;
}
当然,命名空间是可以自定义的,格式如下:
// 注册命名空间
namespace [namespaceName] {
// 声明内容
}
// 使用该命名空间,类似于 std::cout
[namespaceName]::<code>; // <code> 可以是变量或函数
举个例子:
#include <iostream>
namespace first_space { // 注册第一个命名空间
void funcTest() {
std::cout << "Inside first_space" << std::endl;
}
}
namespace second_space { // 注册第二个命名空间
void funcTest() {
std::cout << "Inside second_space" << std::endl;
}
}
int main () {
first_space::funcTest(); // 调用第一个命名空间中的函数
second_space::funcTest(); // 调用第二个命名空间中的函数
return 0;
}
执行结果:
可以看到,调用指定命名空间中的函数需要在函数前增加命名空间标示符,这样即使出现同名函数,也不会互相冲突,所以在实际开发过程中,是非常推荐使用命名空间标示符的,再举个例子:
#include <iostream>
namespace first_space { // 注册第一个命名空间
void funcTest() {
std::cout << "Inside first_space" << std::endl;
}
}
namespace second_space{ // 第二个命名空间
void funcTest() {
std::cout << "Inside second_space" << std::endl;
}
}
using namespace first_space; // 使用第一个命名空间
int main () {
funcTest(); // 调用第一个命名空间中的函数
return 0;
}
执行结果:
可以看到,在主函数 main()
前面进行了命名空间的声明,所以主函数中的同名函数 funcTest()
执行输出的结果是第一个命名空间中的函数,当然C++程序执行时有严格的顺序要求,如果将命名空间的注册语句或者使用语句放在main函数后,那么就会报错,例如:
请严格记住,C++程序的执行顺序必然是从上到下,所以主函数
main()
中的其他函数、变量、方法、对象等必须在主函数前进行注册或声明。同样,函数中的语句也是要严格遵守这一点的。
变量与常量
数据类型
C++七大基本类型:
其中的 wchar_t
其实是利用了 typedef
声明,使用方法如下:
typedef <type> <newname> // 标准格式
// 示例:
typedef short int wchar_t;
// 使用示例
typedef int feet; // 将int类型定义为feet
feet test; // 使用刚才定义的feet来声明一个变量test
一些基本类型可以使用一个或多个类型修饰符进行修饰:
signed unsigned short long
默认情况下,int、short、long都是带符号的,即signed
具体的占用空间大小和长度就不赘述了,下面这个例子可以直接体现:
#include <iostream>
#include <limits>
using namespace std;
int main() {
cout << "type: \t\t" << "************size**************"<< endl;
cout << "bool: \t\t" << "所占字节数:" << sizeof(bool);
cout << "\t最大值:" << (numeric_limits<bool>::max)();
cout << "\t\t最小值:" << (numeric_limits<bool>::min)() << endl;
cout << "char: \t\t" << "所占字节数:" << sizeof(char);
cout << "\t最大值:" << (numeric_limits<char>::max)();
cout << "\t\t最小值:" << (numeric_limits<char>::min)() << endl;
cout << "signed char: \t" << "所占字节数:" << sizeof(signed char);
cout << "\t最大值:" << (numeric_limits<signed char>::max)();
cout << "\t\t最小值:" << (numeric_limits<signed char>::min)() << endl;
cout << "unsigned char: \t" << "所占字节数:" << sizeof(unsigned char);
cout << "\t最大值:" << (numeric_limits<unsigned char>::max)();
cout << "\t\t最小值:" << (numeric_limits<unsigned char>::min)() << endl;
cout << "wchar_t: \t" << "所占字节数:" << sizeof(wchar_t);
//cout << "\t最大值:" << (numeric_limits<wchar_t>::max)();
//cout << "\t\t最小值:" << (numeric_limits<wchar_t>::min)() << endl;
cout << "short: \t\t" << "所占字节数:" << sizeof(short);
cout << "\t最大值:" << (numeric_limits<short>::max)();
cout << "\t\t最小值:" << (numeric_limits<short>::min)() << endl;
cout << "int: \t\t" << "所占字节数:" << sizeof(int);
cout << "\t最大值:" << (numeric_limits<int>::max)();
cout << "\t最小值:" << (numeric_limits<int>::min)() << endl;
cout << "unsigned: \t" << "所占字节数:" << sizeof(unsigned);
cout << "\t最大值:" << (numeric_limits<unsigned>::max)();
cout << "\t最小值:" << (numeric_limits<unsigned>::min)() << endl;
cout << "long: \t\t" << "所占字节数:" << sizeof(long);
cout << "\t最大值:" << (numeric_limits<long>::max)();
cout << "\t最小值:" << (numeric_limits<long>::min)() << endl;
cout << "unsigned long: \t" << "所占字节数:" << sizeof(unsigned long);
cout << "\t最大值:" << (numeric_limits<unsigned long>::max)();
cout << "\t最小值:" << (numeric_limits<unsigned long>::min)() << endl;
cout << "double: \t" << "所占字节数:" << sizeof(double);
cout << "\t最大值:" << (numeric_limits<double>::max)();
cout << "\t最小值:" << (numeric_limits<double>::min)() << endl;
cout << "long double: \t" << "所占字节数:" << sizeof(long double);
cout << "\t最大值:" << (numeric_limits<long double>::max)();
cout << "\t最小值:" << (numeric_limits<long double>::min)() << endl;
cout << "float: \t\t" << "所占字节数:" << sizeof(float);
cout << "\t最大值:" << (numeric_limits<float>::max)();
cout << "\t最小值:" << (numeric_limits<float>::min)() << endl;
cout << "size_t: \t" << "所占字节数:" << sizeof(size_t);
cout << "\t最大值:" << (numeric_limits<size_t>::max)();
cout << "\t最小值:" << (numeric_limits<size_t>::min)() << endl;
cout << "string: \t" << "所占字节数:" << sizeof(string) << endl;
//cout << "\t最大值:" << (numeric_limits<string>::max)();
//cout << "\t最小值:" << (numeric_limits<string>::min)() << endl;
cout << "type: \t\t" << "************size**************"<< endl;
return 0;
}
输出如下:
枚举类型: 是C++中的一种派生数据类型,它是由用户定义的若干枚举常量的集合。如果一个变量只有几种可能的值,可以定义为枚举(enumeration)类型。所谓"枚举"是指将变量的值一一列举出来,变量的值只能在列举出来的值的范围内。
用法:
enum 枚举名{
标识符[=整型常数],
标识符[=整型常数],
标识符[=整型常数]
} 枚举变量;
如果枚举没有初始化, 即省掉 =整型常数
时, 则从第一个标识符开始。
默认情况下,第一个名称的值为 0,第二个名称的值为 1,第三个名称的值为 2,以此类推
举个例子:
enum color { red, green, blue } c;
c = blue;
// 本示例定义了一个颜色枚举,变量c的类型为color,以及c被赋值为"blue"
在该示例中,red
的值为 0
、green
的值为 1
、blue
的值为 2
当然也可以给名称直接赋予值,例如:
enum color {
red,
green = 6,
blue
} c;
c = blue;
此时,red
的值依然为 0
,但是 green
的值为 6
,同时 blue
的值自增1,为 7
类型转换
C++中有四种类型转换:静态转换、动态转换、常量转换和重新解释转换。
静态转换(Static Cast):
静态转换是将一种数据类型的值强制转换为另一种数据类型的值,通常用于比较类型相似的对象之间的转换,例如将 int
类型转换为 float
类型。静态转换不进行任何运行时类型检查,因此可能会导致运行时错误。
// 静态将int类型转换为float类型
int a = 10;
float b = static_cast<float>(a);
动态转换(Dynamic Cast):
动态转换通常用于将一个基类指针或引用转换为派生类指针或引用。动态转换在运行时进行类型检查,如果不能进行转换则返回空指针或引发异常。
// 将基类指针转换为派生类指针
class Base {};
class Derived : public Base {};
Base* ptr_base = new Derived;
Derived* ptr_derived = dynamic_cast<Derived*>(ptr_base);
什么是指针后续再讲,这里先不做具体描述
常量转换(Const Cast):
常量转换用于将 const
类型的对象转换为 非const
类型的对象,且只能用于转换掉const属性,不能改变对象的类型。
// 常量转换,将const int转换为int
const int a = 10;
int& b = const_cast<int&>(a);
重新解释转换(Reinterpret Cast):
重新解释转换将一个数据类型的值重新解释为另一个数据类型的值,通常用于在不同的数据类型之间进行转换。
重新解释转换不进行任何类型检查,因此可能会导致未定义的行为。
// 重新解释将int类型转换为float类型
int a = 10;
float b = reinterpret_cast<float&>(a);
变量类型
变量其实只不过是程序可操作的存储区的名称,在C++中,有多种变量类型可用于存储不同种类的数据。
C++中每个变量都有指定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。
变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下划线开头,且大写字母和小写字母是不同的。
C++常见的变量类型有如下几种:
- 整数类型:
int
short
long
long long
;用于表示整数、短整数、长整数、更长整数。 - 浮点类型:
float
double
long double
;用于表示单精度、双精度、更高精度的浮点数。 - 字符类型:
char
wchar_t
char16_t
char32_t
;用于表示字符、宽字符、16位Unicode字符、32位Unicode字符。 - 布尔类型:
bool
;用于表示布尔值,只能取true或false。 - 枚举类型:
enum
;用于定义一组命名的整数常量。 - 指针类型:
type*
,type
是其他的类型;用于表示指向类型为type的对象的指针。 - 数组类型:
type[]
或type[size]
;用于表示具有相同类型的元素组成的数组。 - 结构体类型:
struct
;用于定义包含多个不同类型成员的结构。 - 类 类型:
class
;用于定义具有属性和方法的自定义类型。 - 共用体类型:
union
;用于定义一种特殊的数据类型,它可以在相同的内存位置存储不同的数据类型。
每种类型占用多少字节不赘述
变量定义与声明
举例:
int i, j, k;
char c, ch;
float f, salary;
double d;
extern int d = 3, f = 5; // d和f的声明
int d = 3, f = 5; // 定义并初始化d和f
byte z = 22; // 定义并初始化z
char x = 'x'; // 变量x 的值为 'x'
关键字
extern
是用来引用在函数后面或另一个文件中的变量
变量赋值举例:
#include <iostream>
using namespace std;
extern int a, b; // 变量声明
extern int c;
extern float f;
int main() {
int a, b; // 变量定义
int c;
float f; // 实际初始化
a = 10;
b = 20;
c = a + b;
cout << c << endl;
f = 70.0/3.0;
cout << f << endl ;
return 0;
}
可以看到在C++中,赋值是右值赋给左值,左值可以为变量,执行结果:
同样的,在函数声明时,提供一个函数名,而函数的实际定义则可以在任何地方进行,例如:
#include <iostream>
using namespace std;
int func(); // 函数声明
int main() {
int i = func(); // 函数调用
cout << i << endl;
}
int func() { // 函数定义
return 0;
}
执行结果:
但是如果在main函数之前没有进行变量/函数声明,而直接在main函数中进行调用的话,是调用不到的且编译会报错,例如:
#include <iostream>
using namespace std;
int main() {
int i = func(); // 函数调用
cout << i << endl;
}
int func() { // 函数定义
return 0;
}
执行结果:
但是可以把函数定义放在函数调用之前即可:
#include <iostream>
using namespace std;
int func() { // 函数定义
return 0;
}
int main() {
int i = func(); // 函数调用
cout << i << endl;
}
这样就可以成功调用了,变量也是同理,在实际开发过程中,我们推荐在main函数前先给函数/变量声明,再在main函数之后进行定义,会使代码更加工整。
变量作用域
一般来说有三个地方可以定义变量:
- 在函数或一个代码块内部声明的变量,称为局部变量;
- 在函数参数的定义中声明的变量,称为形式参数;
- 在所有函数外部声明的变量,称为全局变量。
作用域是程序的一个区域,变量的作用域可以分为以下几种:
- **局部作用域:**在函数内部声明的变量具有局部作用域,它们只能在函数内部访问。局部变量在函数每次被调用时被创建,在函数执行完后被销毁;
- **全局作用域:**在所有函数和代码块之外声明的变量具有全局作用域,它们可以被程序中的任何函数访问。全局变量在程序开始时被创建,在程序结束时被销毁;
- **块作用域:**在代码块内部声明的变量具有块作用域,它们只能在代码块内部访问。块作用域变量在代码块每次被执行时被创建,在代码块执行完后被销毁;
- **类作用域:**在类内部声明的变量具有类作用域,它们可以被类的所有成员函数访问。类作用域变量的生命周期与类的生命周期相同。
如果在内部作用域中声明的变量与外部作用域中的变量同名,则内部作用域中的变量将覆盖外部作用域中的变量
局部变量:
在函数或一个代码块内部声明的变量,称为局部变量,只能被函数内部或者代码块内部的语句使用:
#include <iostream>
using namespace std;
int main() {
int a, b; // 局部变量声明
int c;
a = 10; // 实际初始化
b = 20;
c = a + b;
cout << c << endl;
return 0;
}
全局变量:
在所有函数外部定义的变量(通常是在程序的头部),称为全局变量,可以被任何函数访问,也就是说,全局变量一旦声明,在整个程序中都是可用的:
#include <iostream>
using namespace std;
int g; // 全局变量声明
int main() {
int a, b; // 局部变量声明
a = 10; // 实际初始化
b = 20;
g = a + b;
cout << g << endl;
return 0;
}
局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值:
#include <iostream>
using namespace std;
int g = 20; // 全局变量声明
int main() {
int g = 10; // 局部变量声明
cout << g << endl;
return 0;
}
执行结果:
初始化局部变量和全局变量:
当局部变量被定义时,系统不会对其初始化,必须自行对其初始化。定义全局变量时,系统会自动初始化为下列值:
int
:0;char
:'\0';float
:0;double
:0;pointer
:NULL;
块作用域指的是在代码块内部声明的变量,例:
#include <iostream>
using namespace std;
int main() {
int a = 10; // 函数内局部变量,同时也是块作用域外部变量
{
int a = 20; // 块作用域变量
cout << "块变量: " << a << endl;
}
cout << "外部变量: " << a << endl;
return 0;
}
执行结果:
类作用域:
类作用域指的是在类内部声明的变量,例:
#include <iostream>
class MyClass {
public:
static int class_var; // 类作用域变量
};
int MyClass::class_var = 30;
int main() {
std::cout << "类变量: " << MyClass::class_var << std::endl;
return 0;
}
执行结果:
什么是类,后续再说
常量
常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值。常量就像是常规的变量,只不过常量的值在定义后不能进行修改。
在C++中,有两种简单的定义常量的方式:
使用#define预处理器:
#define [identifier] [value]
举例:
#include <iostream>
using namespace std;
#define LENGTH 10
#define WIDTH 5
#define NEWLINE '\n'
int main() {
int area;
area = LENGTH * WIDTH;
cout << area;
cout << NEWLINE;
cout << area;
return 0;
}
执行结果:
\n
被执行了
使用const关键字:
const [type] [variable] = [value];
举例:
#include <iostream>
using namespace std;
int main() {
const int LENGTH = 10;
const int WIDTH = 5;
const char NEWLINE = '\n';
int area;
area = LENGTH * WIDTH;
cout << area;
cout << NEWLINE;
cout << area;
return 0;
}
执行结果同上,这里需要注意的是,使用 const
关键字并不是一定需要放在函数内部,它可以和 #define
一样作为全局变量来使用
一般来说,在开发过程中把常量定义为全大写字母形式