本文最后更新于 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/Ostream 就是输入/出信息流嘛;如果要引入一些本地库或第三方库,可以通过使用 #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 的值为 0green 的值为 1blue 的值为 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 一样作为全局变量来使用

一般来说,在开发过程中把常量定义为全大写字母形式