[c++] C Language Features
C++中的C特性
菜鸡基础
一、版本历史
C++的基础也可以理解为C的基础。这里主要是复习下其中的相对简单的基础部分。
C++ 是一种中级语言,它是由 Bjarne Stroustrup 于 1979 年在贝尔实验室开始设计开发的。
C++ 进一步扩充和完善了 C 语言,是一种面向对象的程序设计语言。C++ 可运行于多种平台上,如 Windows、MAC 操作系统以及 UNIX 的各种版本。
菜鸡教程:https://www.runoob.com/cplusplus/cpp-tutorial.html
发布时间 | 文档 | 通称 | 备注 |
---|---|---|---|
2015 | ISO/IEC TS 19570:2015 | - | 用于并行计算的扩展 |
2015 | ISO/IEC TS 18822:2015 | - | 文件系统 |
2014 | ISO/IEC 14882:2014 | C++14 | 第四个C++标准 |
2011 | ISO/IEC TR 24733:2011 | - | 十进制浮点数扩展 |
2011 | ISO/IEC 14882:2011 | C++11 | 第三个C++标准 |
2010 | ISO/IEC TR 29124:2010 | - | 数学函数扩展 |
2007 | ISO/IEC TR 19768:2007 | C++TR1 | C++技术报告:库扩展 |
2006 | ISO/IEC TR 18015:2006 | - | C++性能技术报告 |
2003 | ISO/IEC 14882:2003 | C++03 | 第二个C++标准 |
1998 | ISO/IEC 14882:1998 | C++98 | 第一个C++标准 |
二、编译选项
g++ 有些系统默认是使用 C++98,我们可以指定使用 C++11 来编译 main.cpp 文件:
g++ -g -Wall -std=c++11 main.cpp
选项 | 解释 |
---|---|
-ansi | 只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色, 例如 asm 或 typeof 关键词。 |
-c | 只编译并生成目标文件。 |
-DMACRO | 以字符串"1"定义 MACRO 宏。 |
-DMACRO=DEFN | 以字符串"DEFN"定义 MACRO 宏。 |
-E | 只运行 C 预编译器。 |
-g | 生成调试信息。GNU 调试器可利用该信息。 |
-IDIRECTORY | 指定额外的头文件搜索路径DIRECTORY。 |
-LDIRECTORY | 指定额外的函数库搜索路径DIRECTORY。 |
-lLIBRARY | 连接时搜索指定的函数库LIBRARY。 |
-m486 | 针对 486 进行代码优化。 |
-o | FILE 生成指定的输出文件。用在生成可执行文件时。 |
-O0 | 不进行优化处理。 |
-O | 或 -O1 优化生成代码。 |
-O2 | 进一步优化。 |
-O3 | 比 -O2 更进一步优化,包括 inline 函数。 |
-shared | 生成共享目标文件。通常用在建立共享库时。 |
-static | 禁止使用共享连接。 |
-UMACRO | 取消对 MACRO 宏的定义。 |
-w | 不生成任何警告信息。 |
-Wall | 生成所有警告信息。 |
关键字 Reserved word
一、保留字表格
下表列出了 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 |
完整关键字介绍可查阅:C++ 的关键字(保留字)完整介绍
二、重难点讲解
优先级降低:黄色:容易忘,但要懂;绿色:再仔细看看;灰色:暂时不用关心.
1. asm
asm (指令字符串):允许在 C++ 程序中嵌入汇编代码。
#include "stdafx.h" #include <iostream> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { unsigned int a; char inputKey; cout<<"输入一个整数:"<<endl; cin>>a; unsigned int *c = &a;
__asm { mov eax, c; //c中存储的a的地址->eax mov eax, [eax]; //a的值->eax add eax,1; mov a,eax; }
cout<<a<<endl; cin>>inputKey; return 0; }
2. auto
auto(自动,automatic)是存储类型标识符,表明变量"自动"具有本地范围,块范围的变量声明(如for循环体内的变量声明)默认为auto存储类型。
可能在使用cout这类自动判断类型的表达时可以考虑auto。
More details: 如何评价 C++ 11 auto 关键字? : lambda, auto配合decltype (之后需要再仔细学习的知识点)
template <typename T> auto add(T x, T y)->decltype(x) { return x+y; }
int main() { int a = 10; int b = 12; auto c = add(a,b); // 范型编程会用到 std::cout<<c<<std::endl; return 0; }
10. const_cast
const_cast<type_id> (expression)
该运算符用来修改类型的 const 或 volatile (变量不优化) 属性。除了 const 或 volatile 修饰之外, type_id 和 expression 的类型是一样的。(1) 常量指针被转化成"非常量指针",并且仍然指向原来的对象;(2) 常量引用被转换成"非常量引用",并且仍然指向原来的对象;(3) 常量对象被转换成非常量对象。
16. dynamic_cast
dynamic_cast(动态转换),允许在运行时刻进行类型转换,从而使程序能够在一个类层次结构安全地转换类型。dynamic_cast 提供了两种转换方式:(1) 把基类指针转换成派生类指针,(2) 把指向基类的左值转换成派生类的引用。
19. explicit
explicit(显式的)的作用是"禁止单参数构造函数"被用于自动型别转换,其中比较典型的例子就是容器类型。在这种类型的构造函数中你可以将初始长度作为参数传递给构造函数。
20. export
为了访问其他编译单元(如另一代码文件)中的变量或对象,对普通类型(包括基本数据类、结构和类),可以利用关键字 extern,来使用这些变量或对象时;
但是对模板类型,则必须在定义这些模板类对象和模板函数时,使用标准 C++ 新增加的关键字 export(导出)。
21. extern
extern(外部的)声明变量或函数为外部链接,即该变量或函数名在其它文件中可见。被其修饰的变量(外部变量)是静态分配空间的,即程序开始时分配,结束时释放。用其声明的变量或函数应该在别的文件或同一文件的其它地方定义(实现)。在文件内声明一个变量或函数默认为可被外部使用。在 C++ 中,还可用来指定使用另一语言进行链接,这时需要与特定的转换符一起使用。目前仅支持 C 转换标记,来支持 C 编译器链接。使用这种情况有两种形式:
extern "C" 声明语句 extern "C" { 声明语句块 }
31. mutable
通俗地说:在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
mutable(易变的)是 C++ 中一个不常用的关键字。只能用于类的“非静态”和“非常量”数据成员。由于一个对象的状态由该对象的非静态数据成员决定,所以随着数据成员的改变,对象的状态也会随之发生变化。
如果一个类的成员函数被声明为 const 类型,表示该函数不会改变对象的状态,也就是该函数不会修改类的非静态数据成员。但是有些时候需要在该类函数中对类的数据成员进行赋值,这个时候就需要用到 mutable 关键字。
32. namespace
namespace(命名空间)用于在逻辑上组织类,是一种比类大的结构。
34. operator
operator(操作符)用于操作符重载。这是 C++ 中的一种特殊的函数。
38.register
register(寄存器)声明的变量称为寄存器变量,在可能的情况下会直接存放在机器的寄存器中;但对 32 位编译器不起作用,当 global optimizations(全局优化)开的时候,它会做出选择是否放在自己的寄存器中;不过其它与 register 关键字有关的其它符号都对32位编译器有效。
39. reinterpret_cast
用法:
reinpreter_cast<type-id> (expression)
type-id 必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。
45. static_cast
用法:
static_cast < type-id > ( expression )
该运算符把 expression 转换为 type-id 类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
-
- ① 用于类层次结构中基类和子类之间指针或引用的转换。进行上行转换(把子类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。
- ② 用于基本数据类型之间的转换,如把 int 转换成 char,把 int 转换成 enum。这种转换的安全性也要开发人员来保证。
- ③ 把空指针转换成目标类型的空指针。
- ④ 把任何类型的表达式转换成void类?
注意 static_cast 不能转换掉 expression 的 const、volitale、或者 __unaligned 属性。
48. template
template(模板),C++ 中泛型机制(paradigm programming)的实现。
50. throw
throw(抛出)用于实现 C++ 的异常处理机制,可以通过 throw 关键字"抛出"一个异常。
52. try
try(尝试)用于实现 C++ 的异常处理机制。可以在 try 中调用可能抛出异常的函数,然后在 try 后面的 catch 中捕获并进行处理。
54. typeid
指出指针或引用指向的对象的实际派生类型。
int a = 100;
cout<<typeid(a).name()<<endl;
55. typename
typename(类型名字)关键字告诉编译器把一个特殊的名字解释成一个类型。在下列情况下必须对一个 name 使用 typename 关键字:
-
- 1. 一个唯一的name(可以作为类型理解),它嵌套在另一个类型中的。
-
2. 依赖于一个模板参数,就是说:模板参数在某种程度上包含这个name。当模板参数使编译器在指认一个类型时产生了误解。
Goto: C++中typename关键字的使用方法和注意事项
61. volatile
volatile(不稳定的)限定一个对象可被外部进程(操作系统、硬件或并发线程等)改变,声明时的语法如下:
int volatile nVint;
这样的声明是不能达到最高效的,因为它们的值随时会改变,系统在需要时会经常读写这个对象的值。因此常用于像中断处理程序之类的异步进程进行内存单元访问。
Goto: C/C++ Volatile关键词深度剖析
"易变” 特性之后,再让我们接着继续来剖析Volatile的下一个特性:"不可优化” 特性。
62. wchar_t
wchar_t 是宽字符类型,每个 wchar_t 类型占 2 个字节,16 位宽。汉字的表示就要用到 wchar_t。
二、字符常量表达
支持三种方式,一样的结果.
"hello, dear"
--------------------------
"hello, \ \ dear"
--------------------------
"hello, " "d" "ear"
重难点新特性总结
封装 - 保留字
一、类型限定符(encapsulation)
类型限定符提供了变量的额外信息。
限定符 | 含义 |
---|---|
const | const 类型的对象在程序执行期间不能被修改改变。 |
volatile | 修饰符 volatile 告诉编译器不需要优化volatile声明的变量,让程序可以直接从内存中读取变量。对于一般的变量编译器会对变量进行优化,将内存中的变量值放在寄存器中以加快读写效率。 |
restrict | 由 restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。 |
二、存储类
存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。下面列出 C++ 程序中可用的存储类:
- auto
- register(弃用 since c++11)
- static
- extern
- mutable
- thread_local (C++11) ---->
从 C++ 11 开始,auto 关键字不再是 C++ 存储类说明符;
使用 thread_local 说明符声明的变量仅可在它在其上创建的线程上访问。 变量在创建线程时创建,并在销毁线程时销毁。 每个线程都有其自己的变量副本。
thread_local (c++11)
疑惑:thread_local 说明符可以与 static 或 extern 合并?
Ref: thread_local变量
C++中有4种存储周期:
- automatic
- static
- dynamic
- thread
哪些变量可以被声明为thread_local?
- 命名空间下的全局变量
- 类的static成员变量 (注意,静态全局变量 反而限制了其作用域)
- 本地变量
thread_local int x; // A thread-local variable at namespace scope 全局变量
class X { static thread_local std::string s; // A thread-local static class data member 静态成员 };
static thread_local std::string X::s; // The definition of X::s is required 静态成员 void foo() { thread_local std::vector<int> v; // A thread-local local variable 本地变量 }
Ref: thread_local变量
线程外的影响不了线程内的东西,线程内还是能影响到主线程share的thread_local的变量。
#include <thread>
thread_local int g_n = 1;
void f()
{
g_n++;
printf("id=%d, n=%d\n", std::this_thread::get_id(),g_n);
}
void foo()
{
thread_local int i = 0;
printf("id=%d, n=%d\n", std::this_thread::get_id(), i);
i++;
}
void f2()
{
foo();
foo();
}
int main()
{
g_n++;
f(); // 主线程这里自己+1,之后又被t1,t2分别再+1,最后得到3
std::thread t1(f); // 这里的全局的g_n是线程自己的
std::thread t2(f);
t1.join();
t2.join();
f2();
std::thread t4(f2);
std::thread t5(f2);
t4.join();
t5.join();
return 0;
}
局部执行
一、循环
for (auto &elem : 数组)
int my_array[5] = {1, 2, 3, 4, 5};
// 每个数组元素乘于 2 for (int &x : my_array) { x *= 2; cout << x << endl; }
// auto 类型也是 C++11 新标准中的,用来自动获取变量的类型 for (auto &x : my_array) { x *= 2; cout << x << endl; }
二、Lamdba 函数
Lambda 函数
C++11 提供了对匿名函数的支持,称为 Lambda 函数(也叫 Lambda 表达式)。
Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用。
Ref: c++ Lambda函数学习
对于sort这样的函数带来了福音,例如:
#include <algorithm> #include <cmath> void abssort(float *x, unsigned N) { std::sort(x, x + N, [](float a, float b) { return std::abs(a) < std::abs(b); }); }
推断或指定 "返回值"
std::cout << [](float f) { return std::abs(f); } (-3.5); std::cout << [](float f) -> int { return std::abs(f); } (-3.5);
这个语句与前面的不同之处在于,lambda 表达式的返回时不是 float 而是 int。
第一个返回3.5;第二个返回3。
本质:可调用对象模板类
auto g_Lambda = [](int i, int j) { return i + j; }; //匿名函数 此处有分号 int main() { std::function<int(int, int)> f = g_Lambda; cout<<f(2,3); getchar(); return 0; }
"传值" or "传引用"
float f0 = 1.0; std::cout << [=](float f) { return f0 + std::abs(f); } (-3.5); 传值:其输出值是 4.5 --------------------------------------------------------------------------------- float f0 = 1.0; std::cout << [&](float f) { return f0 += std::abs(f); } (-3.5); std::cout << '\n' << f0 << '\n'; 传引用:输出值是 4.5 和 4.5 --------------------------------------------------------------------------------- float f0 = 1.0; std::cout << [=](float f) mutable { return f0 += std::abs(f); } (-3.5); std::cout << '\n' << f0 << '\n';
如果以传值的形式捕获外部变量,那么,lambda 体不允许修改外部变量。 你会觉得输出值是什么呢?答案是,4.5 和 1.0。 --------------------------------------------------------------------------------- float f0 = 1.0f; float f1 = 10.0f; std::cout << [=, &f0](float a) { return f0 += f1 + std::abs(a); } (-3.5); std::cout << '\n' << f0 << '\n'; 混合机制:这个例子的输出是 14.5 和 14.5。
小总结:
[] // 不捕获任何外部变量 [=] // 以值的形式捕获所有外部变量 [&] // 以引用形式捕获所有外部变量 [x, &y] // x 以传值形式捕获,y 以引用形式捕获 [=, &z] // z 以引用形式捕获,其余变量以传值形式捕获 [&, x] // x 以值的形式捕获,其余变量以引用形式捕获
基本类型
一、数字
数学头文件 <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 <ctime> #include <cstdlib> 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; }
二、数组
参数中的数组有size,好处就是”可以重载";平时还是最好加个size的参数。
数组的缺陷 (1):
arr[5]作为参数的话,sizeof(arr)指的是其中一个元素的大小,不是整个数组的。
double getAverage(int *arr, int size); // 形式参数是一个指针: double getAverage(int arr[5]); // 重载函数,形式参数是一个已定义大小的数组: double getAverage2(int arr[]); // 不可重载,形式参数是一个未定义大小的数组:
数组的缺陷 (2):
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<s2 则返回值小于 0;如果 s1>s2 则返回值大于 0。 |
5 | strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 |
6 | strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
四、指针和引用
引用作为"返回值"
double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0}; double& setValues( int i ) { return vals[i]; // 返回第 i 个元素的引用 } // 函数可以放在左边 setValues(1) = 20.23; // 改变第 2 个元素 setValues(3) = 70.8; // 改变第 4 个元素
注意:返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用
引用作为"参数"
Ref: 【C++】为什么不能定义数组的引用,却可定义变量的引用
数组的引用的可以定义的,比如:
int a[10]; int(&ra)[10]=a; // 这个才是正牌的 数组的引用,用sizeof(ra)可以看出来
或者:
// 数组是个地址,那么先定义一个int地址的引用int*& ,数组名有const特性,所以引用也要是个const
int* const& ra=a;
// 不能定义引用数组,就是所有元素都是引用的数组 int& ra[10]; //这个是不行的 ,定义数组时要分配空间,而引用是不占用内存空间的,所以c++规定不可以定义引用数组
这样,引用便可以指代"数组",例子如下:
bool array_assign(int (&p)[3],int (&q)[3])
{
//std::cout<<sizeof(p)<<std::endl;
if(sizeof(p)!=sizeof(q))
{
std::cout<<"The subscript values do not match@!!!"<<std::endl;
return false;
}
for(size_t i=0;i<sizeof(q)/sizeof(q[0]);i++){
p[i]=q[i];
}
return true;
}
int main()
{
int a[3]={2,8,16};
int b[3];
bool rest;
rest = array_assign(b, a);
if(rest)
{
for(size_t i=0;i<3;i++)
{
std::cout<<b[i]<<std::endl;
}
}
}
"指针”的 不可替代性
1.如果一个指针所指向的对象,需要用分支语句加以确定,或者在中途需要改变他所指的对象,那么在它初始化之后需要为他赋值,而引用只能在初始化时指定被引用的对象,所以不能胜任。【过程中需要改变所指时】
2.有时一个指针的值可能是空指针,例如当把指针作为函数的参数类型或返回类型是,有时会用空指针表达特定的含义,而没用空引用之说。【空指针】
3.使用函数指针,由于没有函数引用,所以函数指针无法被引用替代。【函数指针】
4.使用new创建的对象或数组,需要用指针来存储它的地址。【本就是存地址】
5.以数组形式传递大批量数据时,需要用指针类型接受参数。【数组的救星,其实引用也可以】
五、结构体
初始化
struct Point { int x; int y; int z; }; Point p = {1, 2, 3};
拷贝初始化
在拷贝过程中,如果没有自定义拷贝构造函数,系统会提供一个缺省的拷贝构造函数,缺省的拷贝构造函数对于基本类型的成员变量,按字节复制,对于类类型成员变量,调用其相应类型的拷贝构造函数。
但是注意缺省的构造函数却是这样的:缺省拷贝构造函数在拷贝过程中是按字节复制的,对于指针型成员变量只复制指针本身,而不复制指针所指向的目标 -- 浅拷贝。
struct stu { int i; char c; char* p;
stu operator=(stu& stuTmp) { i = stuTmp.i; c = stuTmp.c; p = new char(strlen(stuTmp.p) + 1); strcpy(p, stuTmp.p); return *this; }; }; int main() { struct stu s1,s2; char * str = "rabbit is cute"; s1.i = 345; s1.c = 'y'; s1.p = (char*)str; s2 = s1; # 就成了深拷贝 printf("s2 %d, %c, %s\n", s2.i, s2.c, s1.p); printf("s1 ptr: %d, s2 ptr : %d\n", s1.p, s2.p); }
"结构体指针" 作为函数参数
// 该函数以结构指针作为参数 void printBook( struct Books *book ) { cout << "书标题 : " << book->title <<endl; cout << "书作者 : " << book->author <<endl; cout << "书类目 : " << book->subject <<endl; cout << "书 ID : " << book->book_id <<endl; } Books Book1; // 定义结构体类型 Books 的变量 Book1 printBook( &Book1 );
typedef 美化
typedef struct Books { char title[50]; char author[50]; char subject[100]; int book_id; } Books;
系统内置接口
一、时间日期
Goto 菜鸡教程:https://www.runoob.com/cplusplus/cpp-date-time.html
#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; }
二、基本的输入输出
输入输出
头文件 | 函数和描述 |
---|---|
<iostream> | 该文件定义了 cin、cout、cerr 和 clog 对象,分别对应于标准输入流、标准输出流、非缓冲标准错误流和缓冲标准错误流。 |
<iomanip> | 该文件通过所谓的参数化的流操纵器(比如 setw 和 setprecision),来声明对执行标准化 I/O 有用的服务。 |
<fstream> | 该文件为用户控制的文件处理声明服务。我们将在文件和流的相关章节讨论它的细节。 |
预定义的对象 cout 是 iostream 类的一个实例。cout 对象"连接"到标准输出设备,通常是显示屏。cout 是与流插入运算符 << 结合使用的
预定义的对象 cin 是 iostream 类的一个实例。cin 对象附属到标准输入设备,通常是键盘。cin 是与流提取运算符 >> 结合使用的
预定义的对象 cerr 是 iostream 类的一个实例。cerr 对象附属到标准错误设备,通常也是显示屏,但是 cerr 对象是非缓冲的,且每个流插入到 cerr 都会立即输出。
预定义的对象 clog 是 iostream 类的一个实例。clog 对象附属到标准错误设备,通常也是显示屏,但是 clog 对象是缓冲的。这意味着每个流插入到 clog 都会先存储在缓冲在,直到缓冲填满或者缓冲区刷新时才会输出。
举个栗子:一个简单的权限控制打印,用于程序调试。
/*****************************************************************************/ // ::print priority #define USER_EMERG "0" /* system is unusable */ #define USER_ALERT "1" /* action must be taken immediately */ #define USER_CRIT "2" /* critical conditions */ #define USER_ERR "3" /* error conditions */ #define USER_WARNING "4" /* warning conditions */ #define USER_NOTICE "5" /* normal but significant condition */ #define USER_INFO "6" /* informational */ #define USER_DEBUG "7" /* debug-level messages */ #define USER_DEFAULT USER_NOTICE /* the default kernel loglevel */ // ::print controller #ifdef ENABLE_DSDEBUG #define ENABLE_DSINFO #define dcout std::cout #else #define dcout 0 && std::cout #endif #ifdef ENABLE_DSINFO #define ENABLE_DSDEFAULT #define icout std::cout #else #define icout 0 && std::cout #endif #ifdef ENABLE_DSDEFAULT #define ncout std::cout #define wcout std::cout #define ecout std::cout #define ccout std::cout #define acout std::cout #define ecout std::cout #else #define ncout 0 && std::cout #define wcout 0 && std::cout #define ecout 0 && std::cout #define ccout 0 && std::cout #define acout 0 && std::cout #define ecout 0 && std::cout #endif
文件和流
Ref: https://www.runoob.com/cplusplus/cpp-files-streams.html
要在 C++ 中进行文件处理,必须在 C++ 源代码文件中包含头文件 <iostream> 和 <fstream>。
数据类型 | 描述 |
---|---|
ofstream | 该数据类型表示输出文件流,用于创建文件并向文件写入信息。 |
ifstream | 该数据类型表示输入文件流,用于从文件读取信息。 |
fstream | 该数据类型通常表示文件流,且同时具有 ofstream 和 ifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。 |
End.