1、命名空间
头文件<iostream>
using namespace std; 告诉编译器使用 std 命名空间
1.1 命名空间(namespace)的意思
可能会出现相同命名的类的功能各不相同,在类前面加上 namespace(名字空间) 以示区分。
std::
std 表示是 C++ 的标准命名空间,是 standard(标准)的缩写,就是编译系统自带有的,按 C++ 标准定义好了的。
比如,在使用输出 std::cout 时,如果它达不到我们想要的效果,我们也可以自己定义一个名字空间。取名 myspace,再在这个空间里写一个 cout 函数来实现。调用时,就成了 myspace::cout。
1.2 using namespace std;
这个的作用是曝光整个 std 名称空间,使其中的所有成员皆可直接使用。
1、声明了命名空间 std,后续如果有未指定命名空间的符号,那么默认使用 std,这样就可以使用 cin、cout、vector 等。
2、假设你不使用预处理 using namespace std; 就要加上 std::cin 或者 std::cout。
cin 用于从控制台获取用户输入,cout 用于将数据输出到控制台。
输入流对象cin , cout 是输出流对象,
可以用 >> 和 <<,是因为分别在其类中对相应运算符进行了重载。
1.3 标识符命名
C++ 标识符是用来标识变量、函数、类、模块,或任何其他用户自定义项目的名称。
- 一个标识符以字母 A-Z 或 a-z 或下划线 _ 开始,后跟零个或多个字母、下划线和数字(0-9)。
- C++ 标识符内不允许出现标点字符,比如 @、& 和 %;
- C++ 命名是区分大小写
- 可以使用带有 $ 的标识符
比如:
有效标识符:mohd zara abc move_name a_123 myname50 _temp j a23b9 retVal
1.4 ::的作用
:: 在 C++ 中表示作用域,和所属关系。 :: 是运算符中等级最高的,它分为三种
1.4.1 作用域符号
作用域符号 :: 的前面一般是类名称,后面一般是该类的成员名称,C++ 为例避免不同的类有名称相同的成员而采用作用域的方式进行区分。
比如:A、B两个类,都有成员data。那么分别表示
A::data表示A的成员data
B::data表示B的成员data
1.4.2 全局作用域符号:
当全局变量在局部函数中与其中某个变量重名,那么就可以用 :: 来区分,例如
cpp
char data;
void sleep()
{
char data = 97;//a的ASCII码
::data = 80;//大写字母P的ASCII码
cout << "局部变量:" << data<< endl;
cout << "全局变量:" << ::data << endl;
}
打印结果:
局部变量:a
全局变量:P
要注意的是使用 cout 输出 char 类型数据 ,打印的数据是对应的ASCII码,而非定义的数值
1.4.3 作用域分解运算符
比如声明了一个类 A,类 A 里声明了一个成员函数 void f(),但没有在类的声明里给出f的定义,那么在类外定义 f 时,就要写成 voidA::f(),表示这个 f() 函数是类 A 的成员函数。例如
cpp
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。
1.5 "\n" 与 endl 的区别?
\n表示内容为一个回车符的字符串(不刷新缓冲区)
std::endl 输出一个换行符,并立即刷新缓冲区。(刷新缓冲区)
2、数据类型
2.1 基本数据类型
七种基本的数据类型:
|------|--------------------|-----|
| 类型 | 关键字 | 字节数 |
| 布尔型 | bool | 1 |
| 字符型 | char | 1 |
| 整型 | int | 4 |
| 浮点型 | float | 4 |
| 双浮点型 | double | 8 |
| 无类型 | void | |
| 宽字符型 | wchar_t(short int) | 2 |
默认情况下,int、short、long都是带符号的
2.1.1 C++相关函数与头文件
头文件:<limits>
字节大小函数:sizeof(data type);
取值范围函数:numeric_limits<data type>::max(or min)
3、变量
3.1 变量类型
变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下划线开头。
大写字母和小写字母是不同的,因为 C++ 是大小写敏感的。
整数类型(Integer Types):
- int:用于表示整数,通常占用4个字节。
- short:用于表示短整数,通常占用2个字节。
- long:用于表示长整数,通常占用4个字节。
- long long:用于表示更长的整数,通常占用8个字节。
浮点类型(Floating-Point Types):
- float:用于表示单精度浮点数,通常占用4个字节。
- double:用于表示双精度浮点数,通常占用8个字节。
- long double:用于表示更高精度的浮点数,占用字节数可以根据实现而变化。
字符类型(Character Types):
- char:用于表示字符,通常占用1个字节。
- wchar_t:用于表示宽字符,通常占用2或4个字节。
- char16_t:用于表示16位Unicode字符,占用2个字节。
- char32_t:用于表示32位Unicode字符,占用4个字节。
布尔类型(Boolean Type):
bool:用于表示布尔值,只能取true或false。
枚举类型(Enumeration Types):
enum:用于定义一组命名的整数常量。
指针类型(Pointer Types):
type*:用于表示指向类型为type的对象的指针。
数组类型(Array Types):
type[ ]或type[size]:用于表示具有相同类型的元素组成的数组。
结构体类型(Structure Types):
struct:用于定义包含多个不同类型成员的结构。
类类型(Class Types):
class:用于定义具有属性和方法的自定义类型。
共用体类型(Union Types):
union:用于定义一种特殊的数据类型,它可以在相同的内存位置存储不同的数据类型。
3.2 变量自动转换规则
3.1.1 自动转换规则
1、若参与运算量的类型不同,则先转换成同一类型,然后进行运算。
2、转换按数据长度增加的方向进行,以保证精度不降低。
如int 型和long型运算时,先把 int 转成 long 型后再进行运算。
若两种类型的字节数不同,转换成高字节数类型
若两种类型的字节数相同,有符号转换成无符号类型
3、所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。
4、char型和short型参与运算时,必须先转换成int型。
5、在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型转换为左边量的类型。如果右边量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度:
cpp
int a ;
float b = 1.24;
a = b;
cour << a;
输出:1
3.1.2 强制转换规则
通过类型转换运算实现。
cpp
float b =1.24;
int a =2;
cout << a+(int)b << endl;
输出:3
3.3 变量作用域
三个地方可以定义变量:
函数外部----------------全局变量
函数内部/代码块内部---局部变量
函数参数----------------形参
变量有四种作用域:
|-------|------------------------|---------------------------|
| | 范围 | 生命周期 |
| 全局作用域 | 全局变量 可以被 当下文件的任何函数访问 | 程序开始创建---程序结束销毁 |
| 局部作用域 | 函数内部声明的变量 可以被 当下函数内部访问 | 函数每次被调用时被创建,在函数执行完后被销毁。 |
| 块作用域 | 代码块内部声明的变量只能在代码块内部访问 | 代码块每次被执行时被创建,在代码块执行完后被销毁。 |
| 类作用域 | 类内部声明的变量可以被类的所有成员函数访问 | 类作用域变量的生命周期与类的生命周期相同。 |
ps:如果全局变量和局部变量重名,那么局部变量将覆盖全局变量。但是C++可以使用::来引用全局
cpp
#include<iostream>
using namespace std;
int a = 10;
int main()
{
int a = 20;
cout << ::a << endl; // 10
cout << a << endl; // 20
return 0;
}
3.3.1 局部变量和全局变量的初始化
定义局部变量系统不会初始化,是随机数。
定义全局变量系统会自动初始化:
4、修饰符类型
|------|------------------------------------------------|-------------------|------|
| 数据类型 | 整形 | 字符型 | 双精度型 |
| 修饰符 | signed、unsigned、long 和 short (可以不写int,int是隐含的) | signed 、 unsigned | long |
4.1 类型限定符(存储类)
|----------|------------------------------------------------------------|
| 限定符 | 含义 |
| const | 常量不能修改 |
| volatile | 易变的变量值,每次编译数值去地址中读(常用于多线程) |
| static | 定义静态变量,作用域仅限当前文件/函数,不能被外部访问 |
| register | 频繁使用的变量,保存在CPU中加速访问提高效率 |
| restrict | 由 restrict 修饰的指针是唯一 一种访问它所指向的对象的方式。 |
| mutable | 表示类中的成员变量可以在 const 成员函数中被修改。 |
| explicit | 修饰只有一个参数的类构造函数,表明该构造函数是显式而非隐式。禁止类对象之间的隐式转换,以及禁止隐式调用拷贝构造函数。 |
5、存储类
存储类 定义 C++ 程序中变量/函数的范围(可见性)和生命周期,放在修饰类型之前。列举常见的:
5.1 auto类
这是默认的存储类说明符,通常可以省略不写。
生命周期:auto 指定的变量具有自动存储期,即它们的生命周期仅限于定义它们的块(block)。auto 变量通常在栈上分配。
用于两种情况:
1、声明变量时根据初始化表达式自动推断该变量的类型(很少使用)
cpp
auto f=3.14; //double
auto s("hello"); //const char*
auto z = new auto(9); // int*
auto x1 = 5, x2 = 5.0, x3='r';//错误,必须是初始化为同一类型
2、 声明函数时函数返回值的占位符。
5.2 register类
用于声明变量,并提示编译器将这些变量存储在寄存器中,以便快速访问。使用 register 关键字可以提高程序的执行速度,因为它减少了对内存的访问次数。
注意:在 C++11 及以后的版本中,register 已经是一个废弃的特性,不再具有实际作用。
5.3 static类
5.3.1 修饰局部变量
修饰变量在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。
静态局部变量有以下特点:
- 该变量在全局数据区分配内存;
- 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
- 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
- 它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;
5.3.2 修饰全局变量
static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。
5.3.3 修饰类数据成员
static 用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享。
修饰类的成员变量:
- 静态成员变量是先于类的对象而存在
- 这个类的所有对象共用一个静态成员
- 如果静态成员是公有的,那么可以直接通过类名调用
- 静态成员数据在声明时候类外初始化
static 修饰类的成员方法:
- 静态成员函数是先于类的对象而存在
- 可用类名直接调用(公有)
- 在静态成员函数中没有this指针,所以不能使用非静态成员
5.4 extern类
extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。(很常用)
5.5 const类
1、 const修饰常量:
const修饰的常量代替宏定义: const double PI = 3.1415;
2、 const修饰成员变量:
cpp
const int data;
const 修饰的成员变量必须在构造方法的参数列表初始化,不能在构造函数内部进行赋值操作
const 修饰的成员变量不能被修改。
3、 const修饰成员方法:
cpp
void showData()const{ }
const 修饰的成员函数中不能修改成员变量,不能调用非 const 修饰的函数。
5.5.1 const 相比 #define 的优点:
const 常量有数据类型,而 #define 没有,编译器可以对const进行类型安全检查,而对define只能进行字符替换,没有安全检查,并且在字符替换时候可能导致意想不到的错误。
有些集成化的调试工具可以对 const 常量进行调试,但是不能对宏常量进行调试。
5.5.2 const char*, char const*的区别
从右向左读
cpp
char * const cp; ( * 读成 pointer to )
cp is a const pointer to char (cp是一个常量指针指向char)
指向的对象可变,指针的指向不可变
const char * p(char const * p);
p is a pointer to const char; (cp是一个指针指向常量char)
指向的对象只读,指针的指向可变
const 放在 * 的左侧任意位置,限定了该指针指向的对象是只读的;
const放在 * 的右侧,限定了指针本身是只读的,即不可变的。
const 右边修饰谁,就说明谁是不可变的。
5.6 thread_local类
**作用:**可以将 thread_local 仅应用于数据声明和定义,thread_local 不能用于函数声明或定义。使用 thread_local 说明符声明的变量仅可在它创建的线程上访问。
**周期:**变量在创建线程时创建,并在销毁线程时销毁。 每个线程都有其自己的变量副本。
thread_local 说明符可以与 static 或 extern 合并。
cpp
thread_local int x; // 命名空间下的全局变量
class X
{
static thread_local std::string s; // 类的static成员变量
};
static thread_local std::string X::s; // X::s 是需要定义的
void foo()
{
thread_local std::vector<int> v; // 本地变量
}
学习文章:菜鸟C++ 教程 | 菜鸟教程 (runoob.com)