引入
根据菜鸟教程学习,供自用
打印hello world
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World" << "\n";
return 0;
}
现象
PS C:\Users\86177> cd "d:\DeskTop\cppstudy\" ; if ($?) { g++ -std=c++11 hello.cpp -o a.exe } ; if ($?) { ./a.exe }
Hello World
PS D:\DeskTop\cppstudy>
简介
面向对象开发的四大特性
- 封装(Encapsulation):封装是将数据和方法组合在一起,对外部隐藏实现细节,只公开对外提供的接口。这样可以提高安全性、可靠性和灵活性。
- 继承(Inheritance):继承是从已有类中派生出新类,新类具有已有类的属性和方法,并且可以扩展或修改这些属性和方法。这样可以提高代码的复用性和可扩展性。
- 多态(Polymorphism):多态是指同一种操作作用于不同的对象,可以有不同的解释和实现。它可以通过接口或继承实现,可以提高代码的灵活性和可读性。
- 抽象(Abstraction):抽象是从具体的实例中提取共同的特征,形成抽象类或接口,以便于代码的复用和扩展。抽象类和接口可以让程序员专注于高层次的设计和业务逻辑,而不必关注底层的实现细节。
基本语法
C++ 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。
- 对象 - 对象具有状态和行为。例如:一只狗的状态 - 颜色、名称、品种,行为 - 摇动、叫唤、吃。对象是类的实例。
- 类 - 类可以定义为描述对象行为/状态的模板/蓝图。
- 方法 - 从基本上说,一个方法表示一种行为。一个类可以包含多个方法。可以在方法中写入逻辑、操作数据以及执行所有的动作。
- 即时变量 - 每个对象都有其独特的即时变量。对象的状态是由这些即时变量的值创建的。
标识符
下表列出了 C++ 中的保留字。这些保留字不能作为常量名、变量名或其他标识符名称。
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 |
注释
块注释符(/.../)是不可以嵌套使用的。
#if 0 ... #endif 属于条件编译,0 即为参数。
此外,我们还可以使用 #if 0 ... #endif 来实现注释,且可以实现嵌套,格式为:
#if 0
code
#endif
你可以把 #if 0 改成 #if 1 来执行 code 的代码。
这种形式对程序调试也可以帮助,测试时使用 #if 1 来执行测试代码,发布后使用 #if 0 来屏蔽测试代码。
#if 后可以是任意的条件语句。
下面的代码如果 condition 条件为 true 执行 code1 ,否则执行 code2。
#if condition
code1
#else
code2
#endif
数据类型
类型 | 关键字 |
---|---|
布尔型 | bool |
字符型 | char |
整型 | int |
浮点型 | float |
双浮点型 | double |
无类型 | void |
宽字符型 | wchar_t (typedef short int wchar_t;) |
在每一行后插入一个换行符,<< 运算符用于向屏幕传多个值,
类型 | 位 | 范围 |
---|---|---|
char | 1 个字节 | -128 到 127 或者 0 到 255 |
unsigned char | 1 个字节 | 0 到 255 |
signed char | 1 个字节 | -128 到 127 |
int | 4 个字节 | -2147483648 到 2147483647 |
unsigned int | 4 个字节 | 0 到 4294967295 |
signed int | 4 个字节 | -2147483648 到 2147483647 |
short int | 2 个字节 | -32768 到 32767 |
unsigned short int | 2 个字节 | 0 到 65,535 |
signed short int | 2 个字节 | -32768 到 32767 |
long int | 8 个字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
signed long int | 8 个字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
unsigned long int | 8 个字节 | 0 到 18,446,744,073,709,551,615 |
float | 4 个字节 | 精度型占4个字节(32位)内存空间,+/- 3.4e +/- 38 (~7 个数字) |
double | 8 个字节 | 双精度型占8 个字节(64位)内存空间,+/- 1.7e +/- 308 (~15 个数字) |
long long | 8 个字节 | 双精度型占8 个字节(64位)内存空间,表示 -9,223,372,036,854,775,807 到 9,223,372,036,854,775,807 的范围 |
long double | 16 个字节 | 长双精度型 16 个字节(128位)内存空间,可提供18-19位有效数字。 |
wchar_t | 2 或 4 个字节 | 1 个宽字符 |
typedef声明
typedef int feet;
枚举类型
enum color{red,blue,green}c;
c=blue;
//这里red=0,blue=1,green=2;c被赋值为1;
enum color2{red1,blue1=5,green1};
//这里red1=0,blue1=5,green1=6;(默认情况下,每个名称都会比它前面一名称大1,但red的值依然是0)
类型转换
四种:静态转换、动态转换、常量转换、重新解释转换
- 静态转换(static_cast)
强转,不检查
int a=10;
float b=static_cast<float>(a);//将a转换为float类型
- 动态转换(dynamic_cast)
将一个基类指针或引用转换为派生类指针或引用。
检查,不能转换返回空指针或者引发异常。
class Base{};
class Derived:public Base{};
Base* ptr_base=new Derived;
Derived* ptr_derived=dynamic_cast<Derived*>(ptr_base);
//将基类指针ptr_base转换为派生类指针ptr_derived
- 常量转换(const_cast)
将const类型的对象转换为非const类型
const int a=10;
int& r=const_cast<int&>(a);
//将const int转换为int
- 重新解释转换(reinterpret_cast)
将一个数据类型的值重新解释为另一个数据类型的值,不检查(同静态转换?)
int i=10;
float f=reineterpret_cast<float&>(i);
//重新解释将int类型转换为float类型
静态转换用于在兼容类型之间进行安全转换,编译时会进行检查。
重新类型转换用于在不同类型之间进行底层位模式的重新解释,不进行任何类型检查,风险较高。
变量类型
类型 | 描述 |
---|---|
bool | 1个字节 |
char | 1个字节 |
int | 4个字节 |
float | 4个字节(1位符号,8位指数,23位小数) |
double | 8个字节(1位符号,11位指数,52位小数) |
void | 表示类型的缺失 |
wchar_t | 2或4个字节(宽字符) |
整数类型
- int 4字节
- short 2字节
- long 4字节
- long long 8字节
浮点类型
- float 4字节
- double 8字节
- long double (占用字节数随实现而变化)
字符类型
- char 1字节
- wchar_t 2或4字节
- char16_t 2字节
- char32_t 4字节
布尔类型
- bool 1字节
枚举类型
- enum 用于定义一组命名的整数常量
指针类型
- type* 指向type类型的指针
数组类型
- 类型[常量表达式]
结构体类型
- struct 关键字定义,可以包含不同类型的成员
类类型
- class 用于定义具有数据成员和函数成员的复合类型
共用体类型
- union 定义一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型
不带初始化的定义:带有静态存储持续时间的变量会被隐式初始化为 NULL(所有字节的值都是 0),其他所有变量的初始值是未定义的。
左值和右值
左值:指向内存位置的表达式被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。
右值:术语右值(rvalue)指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说我们不可以把一个右值赋值给一个变量。
(变量是左值,因此可以出现在赋值号的左边。数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边。下面是一个有效的语句)
变量作用域
- 局部变量
- 全局变量
- 形式参量
- 局部作用域
- 全局作用域
- 块作用域
- 类作用域:在类内部声明的变量具有类作用域,它们可以被类的所有成员函数访问。类作用域变量的生命周期与类的生命周期相同。
(如果在内部作用域中声明的变量与外部作用域中的变量同名,则内部作用域中的变量将覆盖外部作用域中的变量。)
当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动初始化为0
,'\0'
,NULL
块作用域举例
#include <iostream>
int main()
{
int a=10;
{
int a=20;
std::cout<<"块变量:"<<a<<std::endl;
}
std::cout<<"块变量:"<<a<<std::endl;
return 0;
}
类作用域举例
class Myclass
{
public:
static int class_var;//类作用域变量
};
int Myclass::class_var=10;
int main()
{
std::cout<<"类变量:"<<Myclass::class_var<<std::endl;
return 0;
}
Myclass类中声明了一个名为class_var的类作用域变量,可以使用类名和作用域解析运算符::
来访问这个变量。
常量
:字面量
整数常量
212 // 合法的 不带前缀则默认表示十进制
215u // 合法的 U表示无符号整数(unsigned)
0xFeeL // 合法的 L表示长整数(long)
078 // 非法的:8不是八进制的数字 前缀0表示八进制
032UU // 非法的:不能重复后缀
85 // 十进制 不带前缀则默认表示十进制
0213 // 八进制 前缀0表示八进制
0x4b // 十六进制 前缀0x或0X表示十六进制
30 // 整数 不带前缀则默认表示十进制
30u // 无符号整数
30l // 长整数
30ul // 无符号长整数
浮点常量
3.14159 // 合法的
314159E-5L // 合法的
510E // 非法的:不完整的指数
210f // 非法的:没有小数或指数
.e55 // 非法的:缺少整数或分数
布尔常量
布尔常量
布尔常量共有两个,它们都是标准的 C++ 关键字:
true 值代表真。
false 值代表假。
我们不应把 true 的值看成 1,把 false 的值看成 0。
字符常量
如果常量以 L(仅当大写时)开头,则表示它是一个宽字符常量(例如 L'x'),此时它必须存储在 wchar_t 类型的变量中
否则,它就是一个窄字符常量(例如 'x'),此时它可以存储在 char 类型的简单变量中
字符常量可以是一个普通的字符(例如 'x')、一个转义序列(例如 '\t'),或一个通用的字符(例如 '\u02C0')。
C++ 中,有一些特定的字符,当它们前面有反斜杠时,它们就具有特殊的含义,被用来表示如换行符(\n)或制表符(\t)等。
转义序列 | 含义 |
---|---|
\ | \ 字符 |
' | ' 字符 |
" | " 字符 |
? | ? 字符 |
\a | 警报铃声 |
\b | 退格键 |
\f | 换页符 |
\n | 换行符 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ooo | 一到三位的八进制数 |
\xhh . . . | 一个或多个数字的十六进制数v |
字符串常量
字符串字面值或常量是括在双引号 "" 中的。
您可以使用 \ 做分隔符,把一个很长的字符串常量进行分行。(只是编写分行,输出没有分行)
定义常量
- 使用#define预处理
#define identifier value
- 使用const关键字
const type variable = value;
const int LENGTH = 10;
const char NEWLINE = '\n';
请注意,把常量定义为大写字母形式,是一个很好的编程实践。
修饰符类型
下面列出了数据类型修饰符:
signed:表示变量可以存储负数。对于整型变量来说,signed 可以省略,因为整型变量默认为有符号类型。
unsigned:表示变量不能存储负数。对于整型变量来说,unsigned 可以将变量范围扩大一倍。
short:表示变量的范围比 int 更小。short int 可以缩写为 short。
long:表示变量的范围比 int 更大。long int 可以缩写为 long。
long long:表示变量的范围比 long 更大。C++11 中新增的数据类型修饰符。
float:表示单精度浮点数。
double:表示双精度浮点数。
bool:表示布尔类型,只有 true 和 false 两个值。
char:表示字符类型。
wchar_t:表示宽字符类型,可以存储 Unicode 字符。
signed int num1 = -10; // 定义有符号整型变量 num1,初始值为 -10
unsigned int num2 = 20; // 定义无符号整型变量 num2,初始值为 20
short int num1 = 10; // 定义短整型变量 num1,初始值为 10
long int num2 = 100000; // 定义长整型变量 num2,初始值为 100000
long long int num1 = 10000000000; // 定义长长整型变量 num1,初始值为 10000000000
float num1 = 3.14f; // 定义单精度浮点数变量 num1,初始值为 3.14
double num2 = 2.71828; // 定义双精度浮点数变量 num2,初始值为 2.71828
bool flag = true; // 定义布尔类型变量 flag,初始值为 true
char ch1 = 'a'; // 定义字符类型变量 ch1,初始值为 'a'
wchar_t ch2 = L'你'; // 定义宽字符类型变量 ch2,初始值为 '你'
类型限定符
限定符 | 含义 |
---|---|
const | const 定义常量,表示该变量的值不能被修改。 |
volatile | 修饰符 volatile 告诉该变量的值可能会被程序以外的因素改变,如硬件或其他线程。 |
restrict | 由 restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。 |
mutable | mutable 用于修饰类的成员变量。被 mutable 修饰的成员变量可以被修改,即使它们所在的对象是 const 的。 |
static | 用于定义静态变量,表示该变量的作用域仅限于当前文件或当前函数内,不会被其他文件或函数访问。 |
register | 用于定义寄存器变量,表示该变量被频繁使用,可以存储在CPU的寄存器中,以提高程序的运行效率。 |
volatile实例
volatile int i = 10; // 定义一个 volatile 整型变量 i,表示其值可能会被程序以外的因素改变。
const实例
const int NUM = 10; // 定义常量 NUM,其值不可修改
const int* ptr = &NUM; // 定义指向常量的指针,指针所指的值不可修改
int const* ptr2 = &NUM; // 和上面一行等价
mutable实例
class Example {
public:
int get_value() const {
return value_; // const 关键字表示该成员函数不会修改对象中的数据成员
}
void set_value(int value) const {
value_ = value; // mutable 关键字允许在 const 成员函数中修改成员变量
}
private:
mutable int value_;
};
register实例
void example_function(register int num) {
// register 关键字建议编译器将变量 num 存储在寄存器中
// 以提高程序执行速度
// 但是实际上是否会存储在寄存器中由编译器决定
}
static实例
void example_function() {
static int count = 0; // static 关键字使变量 count 存储在程序生命周期内都存在
count++;
}
存储类
auto:这是默认的存储类说明符,通常可以省略不写。auto 指定的变量具有自动存储期,即它们的生命周期仅限于定义它们的块(block)。auto 变量通常在栈上分配。
register:用于建议编译器将变量存储在CPU寄存器中以提高访问速度。在 C++11 及以后的版本中,register 已经是一个废弃的特性,不再具有实际作用。
static:用于定义具有静态存储期的变量或函数,它们的生命周期贯穿整个程序的运行期。在函数内部,static变量的值在函数调用之间保持不变。在文件内部或全局作用域,static变量具有内部链接,只能在定义它们的文件中访问。
extern:用于声明具有外部链接的变量或函数,它们可以在多个文件之间共享。默认情况下,全局变量和函数具有 extern 存储类。在一个文件中使用extern声明另一个文件中定义的全局变量或函数,可以实现跨文件共享。
mutable (C++11):用于修饰类中的成员变量,允许在const成员函数中修改这些变量的值。通常用于缓存或计数器等需要在const上下文中修改的数据。
thread_local (C++11):用于定义具有线程局部存储期的变量,每个线程都有自己的独立副本。线程局部变量的生命周期与线程的生命周期相同。
#include <iostream>
// 全局变量,具有外部链接,默认存储类为extern
int globalVar;
void function() {
// 局部变量,具有自动存储期,默认存储类为auto
auto int localVar = 10;
// 静态变量,具有静态存储期,生命周期贯穿整个程序
static int staticVar = 20;
const int constVar = 30; // const变量默认具有static存储期
// 尝试修改const变量,编译错误
// constVar = 40;
// mutable成员变量,可以在const成员函数中修改
class MyClass {
public:
mutable int mutableVar;
void constMemberFunc() const {
mutableVar = 50; // 允许修改mutable成员变量
}
};
// 线程局部变量,每个线程有自己的独立副本
thread_local int threadVar = 60;
}
int main() {
extern int externalVar; // 声明具有外部链接的变量
function();
return 0;
}
auto存储类
自 C++ 11 以来,auto 关键字用于两种情况:声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。
C++98 标准中 auto 关键字用于自动变量的声明,但由于使用极少且多余,在 C++17 中已删除这一用法。
根据初始化表达式自动推断被声明的变量的类型,如:
auto f=3.14; //double
auto s("hello"); //const char*
auto z = new auto(9); // int*
auto x1 = 5, x2 = 5.0, x3='r';//错误,必须是初始化为同一类型
thread_local存储类
thread_local 是 C++11 引入的一种存储类,用于在多线程环境中管理线程特有的变量。
使用 thread_local 修饰的变量在每个线程中都有独立的实例,因此每个线程对该变量的操作不会影响其他线程。
独立性:每个线程都有自己独立的变量副本,不同线程之间的读写操作互不干扰。
生命周期:thread_local 变量在其线程结束时自动销毁。
初始化:thread_local 变量可以进行静态初始化或动态初始化,支持在声明时初始化。
thread_local 适合用于需要存储线程状态、缓存或者避免数据竞争的场景,如线程池、请求上下文等。
以下演示了可以被声明为 thread_local 的变量:
#include <iostream>
#include <thread>
thread_local int threadSpecificVar = 0; // 每个线程都有自己的 threadSpecificVar
void threadFunction(int id) {
threadSpecificVar = id; // 设置线程特有的变量
std::cout << "Thread " << id << ": threadSpecificVar = " << threadSpecificVar << std::endl;
}
int main() {
std::thread t1(threadFunction, 1);
std::thread t2(threadFunction, 2);
t1.join();
t2.join();
return 0;
}
注意事项:
性能:由于每个线程都有独立的副本,thread_local 变量的访问速度可能比全局或静态变量稍慢。
静态存储:thread_local 变量的存储类型为静态存储持续时间,因此在程序整个运行期间会一直存在。
运算符
同C
循环
同C
一般情况下,C++ 程序员偏向于使用 for(;;)
结构来表示一个无限循环。
注意:您可以按 Ctrl + C 键终止一个无限循环。
判断
同C
函数
同C
函数还有很多叫法,比如方法、子例程或程序,等等。
lamba表达式
C++11 提供了对匿名函数的支持,称为 Lambda 函数(也叫 Lambda 表达式)。
Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。
Lambda 表达式本质上与函数声明非常类似。
Lambda 表达式具体形式如下: [capture](parameters)->return-type{body}
eg:[](int x, int y){ return x < y ; }
如果没有返回值可以表示为:[capture](parameters){body}
eg:[]{ ++global_x; }
在一个更为复杂的例子中,返回类型可以被明确的指定如下:[](int x, int y) -> int { int z = x + y; return z + x; }
本例中,一个临时的参数 z 被创建用来存储中间结果。如同一般的函数,z 的值不会保留到下一次该不具名函数再次被调用时。
如果 lambda 函数没有传回值(例如 void),其返回类型可被完全忽略。
在Lambda表达式内可以访问当前作用域的变量,这是Lambda表达式的闭包(Closure)行为。 与JavaScript闭包不同,C++变量传递有传值和传引用的区别。可以通过前面的[]来指定:
[] // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。
另外有一点需要注意。对于[=]或[&]的形式,lambda 表达式可以直接使用 this 指针。但是,对于[]的形式,如果要使用 this 指针,必须显式传入:
[this]() { this->someFunc(); }();
数字
同C
数组
同C
#include <iostream>
using namespace std;
#include <iomanip>
using std::setw;
int main ()
{
int n[ 10 ]; // n 是一个包含 10 个整数的数组
// 初始化数组元素
for ( int i = 0; i < 10; i++ )
{
n[ i ] = i + 100; // 设置元素 i 为 i + 100
}
cout << "Element" << setw( 13 ) << "Value" << endl;
// 输出数组中每个元素的值
for ( int j = 0; j < 10; j++ )
{
cout << setw( 7 )<< j << setw( 13 ) << n[ j ] << endl;
}
return 0;
}
上面的程序使用了 setw() 函数 来格式化输出。当上面的代码被编译和执行时,它会产生下列结果:
Element Value
0 100
1 101
2 102
3 103
4 104
5 105
6 106
7 107
8 108
9 109
概念 | 描述 |
---|---|
多维数组 C++ | 支持多维数组。多维数组最简单的形式是二维数组。 |
指向数组的指针 | 您可以通过指定不带索引的数组名称来生成一个指向数组中第一个元素的指针。 |
传递数组给函数 | 您可以通过指定不带索引的数组名称来给函数传递一个指向数组的指针。 |
从函数返回数组 | C++ 允许从函数返回数组。 |
字符串
同C
字符串实际上是使用 null 字符 \0 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。
其实,您不需要把 null 字符放在字符串常量的末尾。C++ 编译器会在初始化数组时,自动把 \0 放在字符串的末尾。
String类
C++ 标准库提供了 string 类类型,支持上述所有的操作,另外还增加了其他更多的功能。
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string str1 = "runoob";
string str2 = "google";
string str3;
int len ;
// 复制 str1 到 str3
str3 = str1;
cout << "str3 : " << str3 << endl;
// 连接 str1 和 str2
str3 = str1 + str2;
cout << "str1 + str2 : " << str3 << endl;
// 连接后,str3 的总长度
len = str3.size();
cout << "str3.size() : " << len << endl;
return 0;
}
result:
str3 : runoob
str1 + str2 : runoobgoogle
str3.size() : 12
指针
同C
概念 | 描述 |
---|---|
C++ Null 指针 | C++ 支持空指针。NULL 指针是一个定义在标准库中的值为零的常量。 |
C++ 指针的算术运算 | 可以对指针进行四种算术运算:++、--、+、- |
C++ 指针 vs 数组 | 指针和数组之间有着密切的关系。 |
C++ 指针数组 | 可以定义用来存储指针的数组。 |
C++ 指向指针的指针 | C++ 允许指向指针的指针。 |
C++ 传递指针给函数 | 通过引用或地址传递参数,使传递的参数在调用函数中被改变。 |
C++ 从函数返回指针 | C++ 允许函数返回指针到局部变量、静态变量和动态内存分配。 |
引用
引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。
引用vs指针
- 不存在空引用。引用必须连接到一块合法的内存。
- 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
- 引用必须在创建时被初始化。指针可以在任何时间被初始化。
#include <iostream>
using namespace std;
int main ()
{
// 声明简单的变量
int i;
double d;
// 声明引用变量
int& r = i; //r 是一个初始化为 i 的整型引用
double& s = d; //s 是一个初始化为 d 的 double 型引用
i = 5;
cout << "Value of i : " << i << endl;
cout << "Value of i reference : " << r << endl;
d = 11.7;
cout << "Value of d : " << d << endl;
cout << "Value of d reference : " << s << endl;
return 0;
}
result:
Value of i : 5
Value of i reference : 5
Value of d : 11.7
Value of d reference : 11.7
概念 | 描述 |
---|---|
把引用作为参数 | C++ 支持把引用作为参数传给函数,这比传一般的参数更安全。 |
把引用作为返回值 | 可以从 C++ 函数中返回引用,就像返回其他数据类型一样。 |
日期和时间
需要在 C++ 程序中引用<ctime>
头文件。
有四个与时间相关的类型:clock_t、time_t、size_t 和 tm。类型 clock_t、size_t 和 time_t 能够把系统时间和日期表示为某种整数。
结构类型 tm 把日期和时间以 C 结构的形式保存
struct tm {
int tm_sec; // 秒,正常范围从 0 到 59,但允许至 61
int tm_min; // 分,范围从 0 到 59
int tm_hour; // 小时,范围从 0 到 23
int tm_mday; // 一月中的第几天,范围从 1 到 31
int tm_mon; // 月,范围从 0 到 11
int tm_year; // 自 1900 年起的年数
int tm_wday; // 一周中的第几天,范围从 0 到 6,从星期日算起
int tm_yday; // 一年中的第几天,范围从 0 到 365,从 1 月 1 日算起
int tm_isdst; // 夏令时
};
序号 | 函数 & 描述 |
---|---|
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\0。 | |
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(); |
该函数可用于格式化日期和时间为指定的格式。 |
#include <iostream>
#include <ctime>
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;
}
result:
本地日期和时间:Sat Jan 8 20:07:41 2011
UTC 日期和时间:Sun Jan 9 03:07:41 2011
使用tm结构格式化时间
#include <iostream>
#include <ctime>
using namespace std;
int main( )
{
// 基于当前系统的当前日期/时间
time_t now = time(0);
cout << "1970 到目前经过秒数:" << now << endl;
tm *ltm = localtime(&now);
// 输出 tm 结构的各个组成部分
cout << "年: "<< 1900 + ltm->tm_year << endl;
cout << "月: "<< 1 + ltm->tm_mon<< endl;
cout << "日: "<< ltm->tm_mday << endl;
cout << "时间: "<< ltm->tm_hour << ":";
cout << ltm->tm_min << ":";
cout << ltm->tm_sec << endl;
}
result:
1970 到目前时间:1503564157
年: 2017
月: 8
日: 24
时间: 16:42:37
基本的输入输出
C++ 的 I/O 发生在流中,流是字节序列。如果字节流是从设备(如键盘、磁盘驱动器、网络连接等)流向内存,这叫做输入操作。如果字节流是从内存流向设备(如显示屏、打印机、磁盘驱动器、网络连接等),这叫做输出操作。
头文件 | 函数和描述 |
---|---|
<iostream> |
该文件定义了 cin、cout、cerr 和 clog 对象,分别对应于标准输入流、标准输出流、非缓冲标准错误流和缓冲标准错误流。 |
<iomanip> |
该文件通过所谓的参数化的流操纵器(比如 setw 和 setprecision),来声明对执行标准化 I/O 有用的服务。 |
<fstream> |
该文件为用户控制的文件处理声明服务。我们将在文件和流的相关章节讨论它的细节。 |
标准输出流 cout
预定义的对象 cout 是 iostream 类的一个实例。cout 对象"连接"到标准输出设备,通常是显示屏。
cout 是与流插入运算符 << 结合使用的,如下所示:
#include <iostream>
using namespace std;
int main( )
{
char str[] = "Hello C++";
cout << "Value of str is : " << str << endl;
}
reslut:
Value of str is : Hello C++
流插入运算符 << 在一个语句中可以多次使用,如上面实例中所示,endl 用于在行末添加一个换行符。
标准输入流 cin
预定义的对象 cin 是 iostream 类的一个实例。cin 对象附属到标准输入设备,通常是键盘。
cin 是与流提取运算符 >> 结合使用的,如下所示:
#include <iostream>
using namespace std;
int main( )
{
char name[50];
cout << "请输入您的名称: ";
cin >> name;
cout << "您的名称是: " << name << endl;
}
result:
请输入您的名称: cplusplus
您的名称是: cplusplus
cin >> name >> age;
相当于cin >> name;cin >> age;
标准错误流 cerr
预定义的对象 cerr 是 iostream 类的一个实例。cerr 对象附属到标准输出设备,通常也是显示屏,但是 cerr 对象是非缓冲的,且每个流插入到 cerr 都会立即输出。
cerr 也是与流插入运算符 << 结合使用的,如下所示:
#include <iostream>
using namespace std;
int main( )
{
char str[] = "Unable to read....";
cerr << "Error message : " << str << endl;
}
result:
Error message : Unable to read....
标准日志流 clog
预定义的对象 clog 是 iostream 类的一个实例。clog 对象附属到标准输出设备,通常也是显示屏,但是 clog 对象是缓冲的。这意味着每个流插入到 clog 都会先存储在缓冲区,直到缓冲填满或者缓冲区刷新时才会输出。
clog 也是与流插入运算符 << 结合使用的,如下所示:
#include <iostream>
using namespace std;
int main( )
{
char str[] = "Unable to read....";
clog << "Error message : " << str << endl;
}
result:
Error message : Unable to read....
良好的编程实践告诉我们,使用 cerr 流来显示错误消息,而其他的日志消息则使用 clog 流来输出。
结构体
同C
使用成员访问运算符(.)
#include <iostream>
#include <cstring>
using namespace std;
// 声明一个结构体类型 Books
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main( )
{
Books Book1; // 定义结构体类型 Books 的变量 Book1
Books Book2; // 定义结构体类型 Books 的变量 Book2
// Book1 详述
strcpy( Book1.title, "C++ 教程");
strcpy( Book1.author, "Runoob");
strcpy( Book1.subject, "编程语言");
Book1.book_id = 12345;
// Book2 详述
strcpy( Book2.title, "CSS 教程");
strcpy( Book2.author, "Runoob");
strcpy( Book2.subject, "前端技术");
Book2.book_id = 12346;
// 输出 Book1 信息
cout << "第一本书标题 : " << Book1.title <<endl;
cout << "第一本书作者 : " << Book1.author <<endl;
cout << "第一本书类目 : " << Book1.subject <<endl;
cout << "第一本书 ID : " << Book1.book_id <<endl;
// 输出 Book2 信息
cout << "第二本书标题 : " << Book2.title <<endl;
cout << "第二本书作者 : " << Book2.author <<endl;
cout << "第二本书类目 : " << Book2.subject <<endl;
cout << "第二本书 ID : " << Book2.book_id <<endl;
return 0;
}
result:
第一本书标题 : C++ 教程
第一本书作者 : Runoob
第一本书类目 : 编程语言
第一本书 ID : 12345
第二本书标题 : CSS 教程
第二本书作者 : Runoob
第二本书类目 : 前端技术
第二本书 ID : 12346
结构体作为函数参数
#include <iostream>
#include <cstring>
using namespace std;
void printBook( struct Books book );
// 声明一个结构体类型 Books
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main( )
{
Books Book1; // 定义结构体类型 Books 的变量 Book1
Books Book2; // 定义结构体类型 Books 的变量 Book2
// Book1 详述
strcpy( Book1.title, "C++ 教程");
strcpy( Book1.author, "Runoob");
strcpy( Book1.subject, "编程语言");
Book1.book_id = 12345;
// Book2 详述
strcpy( Book2.title, "CSS 教程");
strcpy( Book2.author, "Runoob");
strcpy( Book2.subject, "前端技术");
Book2.book_id = 12346;
// 输出 Book1 信息
printBook( Book1 );
// 输出 Book2 信息
printBook( Book2 );
return 0;
}
void printBook( struct Books book )
{
cout << "书标题 : " << book.title <<endl;
cout << "书作者 : " << book.author <<endl;
cout << "书类目 : " << book.subject <<endl;
cout << "书 ID : " << book.book_id <<endl;
}
result:
书标题 : C++ 教程
书作者 : Runoob
书类目 : 编程语言
书 ID : 12345
书标题 : CSS 教程
书作者 : Runoob
书类目 : 前端技术
书 ID : 12346
结构体中各个部分详解
struct 关键字:用于定义结构体,它告诉编译器后面要定义的是一个自定义类型。
成员变量:成员变量是结构体中定义的数据项,它们可以是任何基本类型或其他自定义类型。在 struct 中,这些成员默认是 public,可以直接访问。
成员函数:结构体中也可以包含成员函数,这使得结构体在功能上类似于类。成员函数可以操作结构体的成员变量,提供对数据的封装和操作。
访问权限:与 class 类似,你可以在 struct 中使用 public、private 和 protected 来定义成员的访问权限。在 struct 中,默认所有成员都是 public,而 class 中默认是 private。
指向结构的指针
定义指向结构的指针struct Books *struct_pointer;
在上述定义的指针变量中存储结构变量的地址struct_pointer = &Book1;
使用指向该结构的指针访问结构的成员,您必须使用 -> 运算符struct_pointer->title;
#include <iostream>
#include <string>
using namespace std;
// 声明一个结构体类型 Books
struct Books
{
string title;
string author;
string subject;
int book_id;
// 构造函数
Books(string t, string a, string s, int id)
: title(t), author(a), subject(s), book_id(id) {}
};
// 打印书籍信息的函数
void printBookInfo(const Books& book) {
cout << "书籍标题: " << book.title << endl;
cout << "书籍作者: " << book.author << endl;
cout << "书籍类目: " << book.subject << endl;
cout << "书籍 ID: " << book.book_id << endl;
}
int main()
{
// 创建两本书的对象
Books Book1("C++ 教程", "Runoob", "编程语言", 12345);
Books Book2("CSS 教程", "Runoob", "前端技术", 12346);
// 输出书籍信息
printBookInfo(Book1);
printBookInfo(Book2);
return 0;
}
reuslt:
书标题 : C++ 教程
书作者 : Runoob
书类目 : 编程语言
书 ID : 12345
书标题 : CSS 教程
书作者 : Runoob
书类目 : 前端技术
书 ID : 12346
typedef关键字
更简单的定义结构的方式,您可以为创建的类型取一个"别名"
typedef struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
}Books;
现在可以Books Book1, Book2;
这样定义结构体变量了
结构体与类的区别
在 C++ 中,struct 和 class 本质上非常相似,唯一的区别在于默认的访问权限:
struct 默认的成员和继承是 public。
class 默认的成员和继承是 private。
在实践中,大多数程序员倾向于使用 struct 来表示数据结构(例如,一个点或矩形),而使用 class 来实现面向对象编程的特性。
结构体与函数的结合
可以通过构造函数初始化结构体,还可以通过引用传递结构体来避免不必要的拷贝。
struct Books {
string title;
string author;
string subject;
int book_id;
// 构造函数
Books(string t, string a, string s, int id)
: title(t), author(a), subject(s), book_id(id) {}
void printInfo() const {
cout << "书籍标题: " << title << endl;
cout << "书籍作者: " << author << endl;
cout << "书籍类目: " << subject << endl;
cout << "书籍 ID: " << book_id << endl;
}
};
void printBookByRef(const Books& book) {
book.printInfo();
}
vector容器
需要#include <vector>
一种序列容器,它允许你在运行时动态地插入和删除元素。
vector 是基于数组的数据结构,但它可以自动管理内存,这意味着你不需要手动分配和释放内存。
基本特性
动态大小:vector 的大小可以根据需要自动增长和缩小。
连续存储:vector 中的元素在内存中是连续存储的,这使得访问元素非常快速。
可迭代:vector 可以被迭代,你可以使用循环(如 for 循环)来访问它的元素。
元素类型:vector 可以存储任何类型的元素,包括内置类型、对象、指针等。
使用场景:
当你需要一个可以动态增长和缩小的数组时。
当你需要频繁地在序列的末尾添加或移除元素时。
当你需要一个可以高效随机访问元素的容器时。
//创建一个 vector 可以像创建其他变量一样简单:
std::vector<int> myVector; // 创建一个存储整数的空 vector
//这将创建一个空的整数向量,也可以在创建时指定初始大小和初始值:
std::vector<int> myVector(5); // 创建一个包含 5 个整数的 vector,每个值都为默认值(0)
std::vector<int> myVector(5, 10); // 创建一个包含 5 个整数的 vector,每个值都为 10
//或
std::vector<int> vec; // 默认初始化一个空的 vector
std::vector<int> vec2 = {1, 2, 3, 4}; // 初始化一个包含元素的 vector
//添加元素
//可以使用 push_back 方法向 vector 中添加元素:
myVector.push_back(7); // 将整数 7 添加到 vector 的末尾
//访问元素
//可以使用下标操作符 [] 或 at() 方法访问 vector 中的元素:
int x = myVector[0]; // 获取第一个元素
int y = myVector.at(1); // 获取第二个元素
//获取大小
//可以使用 size() 方法获取 vector 中元素的数量:
int size = myVector.size(); // 获取 vector 中的元素数量
//迭代访问
//可以使用迭代器遍历 vector 中的元素:
for (auto it = myVector.begin(); it != myVector.end(); ++it) {
std::cout << *it << " ";
}
//或使用范围 for 循环
for (int element : myVector) {
std::cout << element << " ";
}
//删除元素
//可以使用 erase() 方法删除 vector 中的元素:
myVector.erase(myVector.begin() + 2); // 删除第三个元素
//清空
//可以使用 clear() 方法清空 vector 中的所有元素:
myVector.clear(); // 清空 vector
- 示例
#include <iostream>
#include <vector>
int main() {
// 创建一个空的整数向量
std::vector<int> myVector;
// 添加元素到向量中
myVector.push_back(3);
myVector.push_back(7);
myVector.push_back(11);
myVector.push_back(5);
// 访问向量中的元素并输出
std::cout << "Elements in the vector: ";
for (int element : myVector) {
std::cout << element << " ";
}
std::cout << std::endl;
// 访问向量中的第一个元素并输出
std::cout << "First element: " << myVector[0] << std::endl;
// 访问向量中的第二个元素并输出
std::cout << "Second element: " << myVector.at(1) << std::endl;
// 获取向量的大小并输出
std::cout << "Size of the vector: " << myVector.size() << std::endl;
// 删除向量中的第三个元素
myVector.erase(myVector.begin() + 2);
// 输出删除元素后的向量
std::cout << "Elements in the vector after erasing: ";
for (int element : myVector) {
std::cout << element << " ";
}
std::cout << std::endl;
// 清空向量并输出
myVector.clear();
std::cout << "Size of the vector after clearing: " << myVector.size() << std::endl;
return 0;
}
result
Elements in the vector: 3 7 11 5
First element: 3
Second element: 7
Size of the vector: 4
Elements in the vector after erasing: 3 7 5
Size of the vector after clearing: 0
- 实例2
#include <bits/stdc++.h>
using namespace std;
int main()
{
// 定义一个整数向量 inner,用于存储乘法表的每一项
vector<int> inner;
// 计数器 count,用于追踪输出向量中的元素位置
int count = 0;
// 生成 1 到 9 的乘法表
for (int i = 1; i < 10; i++) {
// 内层循环,用于输出每一行的乘法结果
for (int j = 1; j <= i; j++) {
inner.push_back(i * j); // 将乘法结果存储到 inner 向量中
cout << setw(4) << inner[count++]; // 输出当前存储的乘法结果,格式化为宽度为 4 的字段
}
cout << endl; // 换行,开始输出下一行的乘法结果
}
system("pause"); // 暂停程序,等待用户按键
return 0;
}
数据结构
同C
- 数组
- 结构体
- 类
类是 C++ 中用于面向对象编程的核心结构,允许定义成员变量和成员函数。与 struct 类似,但功能更强大,支持继承、封装、多态等特性。
特点:
可以包含成员变量、成员函数、构造函数、析构函数。
支持面向对象特性,如封装、继承、多态。
class Person {
private:
string name;
int age;
public:
Person(string n, int a) : name(n), age(a) {}
void printInfo() {
cout << "Name: " << name << ", Age: " << age << endl;
}
};
Person p("Bob", 30);
p.printInfo(); // 输出: Name: Bob, Age: 30
- 链表
链表是一种动态数据结构,由一系列节点组成,每个节点包含数据和指向下一个节点的指针。
特点:
动态调整大小,不需要提前定义容量。
插入和删除操作效率高,时间复杂度为 O(1)(在链表头部或尾部操作)。
线性查找,时间复杂度为 O(n)。
struct Node {
int data;
Node* next;
};
Node* head = nullptr;
Node* newNode = new Node{10, nullptr};
head = newNode; // 插入新节点
- 栈
栈是一种后进先出(LIFO, Last In First Out)的数据结构,常用于递归、深度优先搜索等场景。
特点:
只允许在栈顶进行插入和删除操作。
时间复杂度为 O(1)。
stack<int> s;
s.push(1);
s.push(2);
cout << s.top(); // 输出 2
s.pop();
- 队列
队列是一种先进先出(FIFO, First In First Out)的数据结构,常用于广度优先搜索、任务调度等场景。
特点:
插入操作在队尾进行,删除操作在队头进行。
时间复杂度为 O(1)。
queue<int> q;
q.push(1);
q.push(2);
cout << q.front(); // 输出 1
q.pop();
- 双端队列
双端队列允许在两端进行插入和删除操作,是栈和队列的结合体。
特点:
允许在两端进行插入和删除。
时间复杂度为 O(1)。
deque<int> dq;
dq.push_back(1);
dq.push_front(2);
cout << dq.front(); // 输出 2
dq.pop_front();
- 哈希表
哈希表是一种通过键值对存储数据的数据结构,支持快速查找、插入和删除操作。C++ 中的 unordered_map 是哈希表的实现。
特点:
使用哈希函数快速定位元素,时间复杂度为 O(1)。
不保证元素的顺序。
unordered_map<string, int> hashTable;
hashTable["apple"] = 10;
cout << hashTable["apple"]; // 输出 10
- 映射 map
map 是一种有序的键值对容器,底层实现是红黑树。与 unordered_map 不同,它保证键的顺序,查找、插入和删除的时间复杂度为 O(log n)。
特点:
保证元素按键的顺序排列。
使用二叉搜索树实现。
map<string, int> myMap;
myMap["apple"] = 10;
cout << myMap["apple"]; // 输出 10
- 集合 set
set 是一种用于存储唯一元素的有序集合,底层同样使用红黑树实现。它保证元素不重复且有序。
特点:
保证元素的唯一性。
元素自动按升序排列。
时间复杂度为 O(log n)。
set<int> s;
s.insert(1);
s.insert(2);
cout << *s.begin(); // 输出 1
- 动态数组 vector
vector 是 C++ 标准库提供的动态数组实现,可以动态扩展容量,支持随机访问。
特点:
动态调整大小。
支持随机访问,时间复杂度为 O(1)。
当容量不足时,动态扩展,时间复杂度为摊销 O(1)。
vector<int> v;
v.push_back(1);
v.push_back(2);
cout << v[0]; // 输出 1