重新系统一下学习这门高深的C++编程语言
Reference: https://www.runoob.com/cplusplus/cpp-tutorial.html
简单回顾一下基础:
std::endl和'\n'的区别是什么
"\n" 表示内容为一个回车符的字符串。std::endl 是流操作子,输出的作用和输出 "\n" 类似,但可能略有区别。
std::endl 输出一个换行符,并立即刷新缓冲区。
例如:
std::cout << std::endl;
相当于:
std::cout << '\n' << std::flush;
或者
std::cout << '\n'; std::fflush(stdout);
或者
std::cout << '\n'; std::fflush(stdout);
由于流操作符 << 的重载,对于 '\n' 和 "\n",输出效果相同。
对于有输出缓冲的流(例如cout、clog),如果不手动进行缓冲区刷新操作,将在缓冲区满后自动刷新输出。不过对于 cout 来说(相对于文件输出流等),缓冲一般体现得并不明显。但是必要情况下使用 endl 代替 '\n' 一般是个好习惯。
对于无缓冲的流(例如标准错误输出流cerr),刷新是不必要的,可以直接使用 '\n'。
.h 和 .cpp是什么?
.h
文件是头文件,通常包含类的声明、函数原型、宏定义以及其他需要在多个源文件之间共享的代码。头文件中的内容可以被其他源文件包含并使用。通常情况下,头文件中不应该包含具体的实现代码,而是应该包含类、函数、变量的声明。.cpp
文件是源文件,通常包含类的实现、函数的具体实现以及其他代码的具体实现。在.cpp
文件中,您可以编写类的方法、函数的实现以及其他代码的具体逻辑。
通常情况下,一个类的声明会放在一个 .h
文件中,而该类的实现会放在一个对应的 .cpp
文件中。这种组织方式有助于提高代码的可读性和可维护性,同时也方便了代码的重用和扩展。
为什么要使用 using namespace std?
让我们来逐词理解:
- using:引用
- namespace:命名空间(名字空间)
- std:标准(standard,一个命名空间的名字,cout、endl等都依靠这个命名空间)
简要来说:
命名空间在多人合作的时候很有用,因为你定义了变量 a,别人也定义了变量 a,这样就重复定义了。如果你在自己的命名空间中定义了 a,别人在别人的命名空间中定义了 a,这样就不重复了,比如:
using namespace xx;
using namespace yy;
xx::a 和 yy::a 虽然都叫 a,但是不是同一个变量。
std 是系统标准的命名空间,为了和用户定义的名字不重复,所以它声明在 std 这个命名空间中。另外,这个空间也像一个大包一样,包括了系统所有的支持。
:: 在 C++ 中表示作用域,和所属关系。 :: 是运算符中等级最高的,它分为三种,分别如下:
一、作用域符号:
作用域符号 :: 的前面一般是类名称,后面一般是该类的成员名称,C++ 为例避免不同的类有名称相同的成员而采用作用域的方式进行区分。
例如:A,B 表示两个类,在 A,B 中都有成员 member。
那么:
- 1、A::member就表示类A中的成员member。
- 2、B::member就表示类B中的成员member。
二、全局作用域符号:
全局作用域符号:当全局变量在局部函数中与其中某个变量重名,那么就可以用 :: 来区分,例如:
char zhou; //全局变量
void sleep()
{
char zhou; //全局变量
char(局部变量) = char(局部变量)*char(局部变量);
::char(全局变量) =::(全局变量) *char(全局变量)
}
三、作用域分解运算符:
:: 是 C++ 里的作用域分解运算符,“比如声明了一个类 A,类 A 里声明了一个成员函数 void f(),但没有在类的声明里给出f的定义,那么在类外定义 f 时,就要写成 voidA::f(),表示这个 f() 函数是类 A 的成员函数。例如:
class CA
{
public:
int ca_var;
int add(int a, int b);
int add(int a);
}
//那么在实现这个函数时,必须这样写:
int CA::add(int a, int b)
{
return a + b;
}
//另外,双冒号也常常用于在类变量内部作为当前类实例的元素进行表示,比如:
int CA::add(int a)
{
return a + ::ca_var;
}
//表示当前类实例中的变量ca_var。
typedef 声明
您可以使用 typedef 为一个已有的类型取一个新的名字。下面是使用 typedef 定义一个新类型的语法:
typedef type newname;
例如,下面的语句会告诉编译器,feet 是 int 的另一个名称:
typedef int feet;
现在,下面的声明是完全合法的,它创建了一个整型变量 distance:
feet distance;
#include<bits/stdc++.h>
using namespace std;
typedef int feet;
typedef long long ll;
int main(){
feet x = 5;
ll y = 1e16 + 10;
cout << x << endl;
cout << y << endl;
return 0;
}
//5
//10000000000000010
枚举类型enum
枚举类型(enumeration)是C++中的一种派生数据类型,它是由用户定义的若干枚举常量的集合。
如果一个变量只有几种可能的值,可以定义为枚举(enumeration)类型。所谓"枚举"是指将变量的值一一列举出来,变量的值只能在列举出来的值的范围内。
创建枚举,需要使用关键字 enum。枚举类型的一般形式为:
enum 枚举名{ 标识符[=整型常数], 标识符[=整型常数], ... 标识符[=整型常数] } 枚举变量;
如果枚举没有初始化, 即省掉"=整型常数"时, 则从第一个标识符开始。
例如,下面的代码定义了一个颜色枚举,变量 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,但 red 的值依然为 0。
注释:枚举类型不一定在main中定义!!!
#include <bits/stdc++.h>
using namespace std;
enum Weekday {
Sunday, // 0
Monday = 5,
Tuesday, // 6
Wednesday, // 7
Thursday,
Friday,
Saturday
};
int main(){
Weekday today = Wednesday;
cout << today << endl;
return 0;
}
// output: 7
静态转换(Static Cast)
静态转换是将一种数据类型的值强制转换为另一种数据类型的值。
静态转换通常用于比较类型相似的对象之间的转换,例如将 int 类型转换为 float 类型。
静态转换不进行任何运行时类型检查,因此可能会导致运行时错误。
#include <bits/stdc++.h>
using namespace std;
int main(){
int x = 9;
double y = static_cast<double>(x);
cout << fixed << setprecision(6) << y * 1.0 << endl; // 设置精度保留6位小数
return 0;
}
// 9.000000
在使用迭代器的时候,也可以用静态类型转换,比如在获得容器的下标的时候
#include<bits/stdc++.h>
using namespace std;
int main(){
vector<int> nums = {-9, 1100, 43, 4, 300, 20};
sort(nums.begin(), nums.end());
//sort后数组为: -9 4 20 43 300 1100
auto left = lower_bound(nums.begin(), nums.end(), 20);
auto right = upper_bound(nums.begin(), nums.end(), 1000);
cout <<"下标是:"<<static_cast<int>(left - nums.begin())<< " 数值是 :" << *left << endl;
cout <<"下标是:"<<static_cast<int>(right - nums.begin())<< " 数值是 :" << *right << endl;
//下标是:2 数值是 :20
//下标是:5 数值是 :1100
return 0;
}
局部变量和全局变量以及类作用域
在程序中,局部变量和全局变量的名称可以相同。
但是在函数内的局部变量与全局变量是两个独立的变量,互不影响。
下述代码中,全局变量定义了一个int g=99,局部变量定义了一个int g=10,由于这两个g所在的作用域不同,所以各自独立。
#include <bits/stdc++.h>
using namespace std;
// 全局变量声明
int g = 99;
// 函数声明
int func();
int main()
{
// 局部变量声明
int g = 10;
cout << g << endl; // 10
int kk = func();
cout << kk << endl; // 99
return 0;
}
// 函数定义
int func()
{
return g;
}
全局变量和和局部变量同名时,可通过域名在函数中引用到全局变量,不加域名解析则引用局部变量
#include<bits/stdc++.h>
using namespace std;
int a = 100;
int main(){
int a = 10;
cout << a << endl; // 10
cout << ::a << endl; // 100
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int var = -1; // 全局变量初始化
int main(){
::var = 1; // 全局变量重新赋值
for(; ::var <= 10; ::var++){
cout << "全局变量var =" << ::var << endl;
}
return 0;
}
//全局变量var =1
//全局变量var =2
//全局变量var =3
//全局变量var =4
//全局变量var =5
//全局变量var =6
//全局变量var =7
//全局变量var =8
//全局变量var =9
//全局变量var =10
类作用域:
这段代码定义了一个名为 Fruit 的类,其中包含一个静态成员变量 price,并将其初始化为 100。静态成员变量是类的所有对象共享的,因此无论创建多少个 Fruit 类的对象,它们都共享同一个 price 变量。
在 main 函数中,首先创建了一个 apple 对象,然后创建了一个 banana 对象。由于 price 是静态变量,apple 和 banana 对象都共享同一个 price 变量,因此无论输出 apple.price 还是 banana.price,最终的输出结果都是 100。
#include<bits/stdc++.h>
using namespace std;
class Fruit{
public:
static int price;
};
int Fruit::price = 100;
int main(){
Fruit apple;
Fruit banana;
cout << apple.price << endl; // 100
cout << banana.price << endl; // 100
return 0;
}
如果要建立一个动态变量(即每个对象都有自己的变量而不是共享一个变量),可以将变量声明为类的非静态成员变量,并在每个对象的构造函数中对其进行初始化。这样,每个对象都会有自己的变量。
例如,修改 Fruit 类,将 price 变量声明为非静态成员变量,并在构造函数中初始化:
#include<bits/stdc++.h>
using namespace std;
class Fruit{
public:
int price; // 非静态成员变量
Fruit():price(0){} // 构造函数初始化 price
};
int main(){
Fruit apple;
apple.price = 100;
Fruit banana;
banana.price = 10;
cout << apple.price << endl; // 输出100
cout << banana.price << endl; // 输出10
return 0;
}
常量
整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。
整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。
下面列举几个整数常量的实例:
#include<bits/stdc++.h>
using namespace std;
int x = 0xFABCD; // x 被初始化为十六进制数 0xFABCD,其十进制值为 1022189
int y = 256L; // y 被初始化为长整型常量 256L,表示为十进制的 256
int z = 412415ul; // z 被初始化为无符号长整型常量 412415ul,表示为十进制的 412415
const double pi = 3.1415926;
#define Length 100 // 宏定义中不应该以分号结尾
#define Width 0.8
int main(){
cout << x << endl;
cout << y << endl;
cout << z << endl;
cout << pi << endl; // 默认情况下 cout 的输出精度是有限的。为了输出更多小数位数,可以使用 std::setprecision 设置输出精度
cout << fixed << setprecision(10) << pi << endl;
double area = Length * Width;
cout << area << endl;
return 0;
}
C++中逻辑运算符以及杂项运算符
假设变量 A 的值为 60,变量 B 的值为 13,则:A = 0011 1100, B = 0000 1101
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与操作,按二进制位进行"与"运算。运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1; | (A & B) 将得到 12,即为 0000 1100 |
| | 按位或运算符,按二进制位进行"或"运算。运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1; | (A | B) 将得到 61,即为 0011 1101 |
^ | 异或运算符,按二进制位进行"异或"运算。运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0; | (A ^ B) 将得到 49,即为 0011 0001 |
~ | 取反运算符,按二进制位进行"取反"运算。运算规则:~1=-2; ~0=-1; | (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。 |
<< | 二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。 | A << 2 将得到 240,即为 1111 0000 |
>> | 二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。 | A >> 2 将得到 15,即为 0000 1111 |
下表列出了 C++ 支持的其他一些重要的运算符。
运算符 | 描述 |
---|---|
sizeof | sizeof 运算符返回变量的大小。例如,sizeof(a) 将返回 4,其中 a 是整数。 |
Condition ? X : Y | 条件运算符。如果 Condition 为真 ? 则值为 X : 否则值为 Y。 |
, | 逗号运算符会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。 |
.(点)和 ->(箭头) | 成员运算符用于引用类、结构和共用体的成员。 |
Cast | 强制转换运算符把一种数据类型转换为另一种数据类型。例如,int(2.2000) 将返回 2。 |
& | 指针运算符 & 返回变量的地址。例如 &a; 将给出变量的实际地址。 |
* | 指针运算符 * 指向一个变量。例如,*var; 将指向变量 var。 |
C++数学运算
在 C++ 中,除了可以创建各种函数,还包含了各种有用的函数供您使用。这些函数写在标准 C 和 C++ 库中,叫做内置函数。您可以在程序中引用这些函数。
C++ 内置了丰富的数学函数,可对各种数字进行运算。下表列出了 C++ 中一些有用的内置的数学函数。
为了利用这些函数,您需要引用数学头文件 <cmath>。
序号 | 函数 & 描述 |
---|---|
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); 该函数返回一个小于或等于传入参数的最大整数。 |
#include <iostream>
#include <cmath>
using namespace std;
int main ()
{
// 数字定义
short s = 10;
int i = -1000;
long l = 100000;
float f = 230.47;
double d = 200.374;
// 数学运算
cout << "sin(d) :" << sin(d) << endl;
cout << "abs(i) :" << abs(i) << endl;
cout << "floor(d) :" << floor(d) << endl;
cout << "sqrt(f) :" << sqrt(f) << endl;
cout << "pow( d, 2) :" << pow(d, 2) << endl;
return 0;
}
//sin(d) :-0.634939
//abs(i) :1000
//floor(d) :200
//sqrt(f) :15.1812
//pow( d, 2) :40149.7
C++生成随机数
srand((unsigned)time(NULL));
:这行代码设置了随机数生成器的种子。time(NULL)
返回当前系统时间的秒数,将其转换为unsigned
类型作为rand
函数的种子,以保证每次运行程序时生成的随机数序列都不同。
#include<bits/stdc++.h>
using namespace std;
int main(){
srand((unsigned)time(NULL)); // 设置随机数种子
int j;
for(int i = 0; i < 10; i++){
j = rand();
cout << "随机数:" << j << endl;
}
return 0;
}