C++变量类型

变量类型其实不用过多阐述,和数据类型相似,变量仅仅是可操作存储区的名称而已,C++还有其他类型的变量,如枚举、引用、数据结构和类等。

枚举类型:enum,用于定义一组命名的整数常量。

变量声明

声明是向编译器保证变量是按照给定的类型和名称而存在,声明只有在编译时才有意义,声明和定义是不同的概念,声明可以多次,而变量的定义只有一次;多个文件且只有一个文件定义变量,变量的声明就显得非常有用,使用extern关键字在任意一个地方声明一个变量。

extern 的用法:

1
2
3
4
5
6
7
8
9
10
11
extern int i;	//声明,不是定义
int i; //声明,也是定义,未初始化
//1.extern告诉编译器变量在其他地方被定义

extern double pi = 3.1415926; //定义,声明加上初始化被当作定义,即使加了extern
//2.带初始化的声明必定是定义,当extern在函数外部时,才可以被初始化

extern double max(double a, double b); //声明
//3.函数的定义和声明就比较简单,{}是定义,否则就是声明

//4.除非有extern,不然都是定义

左值和右值

这两个概念和C中是一样的,只需要注意右值不能出现在赋值号的左边。

C++变量作用域

定义变量的三个地方:

1.函数和代码块的内部,局部变量

2.函数参数的定义中声明的变量,形式参数

3.函数外部声明的变量,全局变量

作用域一般分为以下几种:

局部作用域:变量只能在函数内部访问,函数调用时创建,执行结束销毁。

全局作用域:变量可以被程序中任何函数访问,程序开始时创建,程序结束销毁。

块作用域:例如循环,只能在代码块中被访问,循环中创建,循环结束而销毁。

类作用域:在类中内部声明的变量,可以被内部所有的成员函数访问,与类生命周期一致。

Tip:若是内部作用域的变量和外部作用域的变量重名,内部变量会将外部变量覆盖。

C++常量

常量是固定值,在程序执行期间不会改变,就像是常规的变量,只是值不能变,常量可以是任何的数据类型。

整数常量:可以是十进制、八进制和十六进制,整数常量也可以带后缀,U表示无符号整数,L表示长整数。

浮点常量:由整数部分、小数点和小数部分或指数部分组成,主要是小数形式和指数形式表示。

布尔常量:属于关键字,只有true 和 false,不应该看作0和1。

字符常量:常常是在单引号中,还有就是一些带反斜杠的转义字符。

字符串常量:通常在双引号””之中,包含字符常量的字符、转义序列和通用字符。

定义常量

C++和C中并没有很大的区别

1.#define预处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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;
return 0;
}

2.const关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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;
return 0;
}

Tip:常量定义一般使用大写字母。

C++修饰符类型

修饰符前面已经阐述过了,其实主要的差异就是集中在signed和unsigned两个修饰符。

C++类型限定符

类型限定符提供了变量的额外信息,用于定义时改变默认行为的关键字。

const:只读变量,值不能被修改。

volatile:该变量的值可能会被程序外的因素改变,硬件和其他线程等。

restrict:该关键字修饰的指针是唯一访问它所指向对象的方式。

mutable:该关键字用于修饰类的成员变量,该变量可以被修改,即使对象是const

static:用于定义静态变量,说明只能用于当前文件或者当前函数。

register:定义寄存器变量,说明其需要被频繁使用,存在寄存器中,提高效率。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const int num = 10;

volatile int num1 = 20;

class Example {
public:
int get_value() const {
return value_; // const 关键字表示该成员函数不会修改对象中的数据成员
}
void set_value(int value) const {
value_ = value; // mutable 关键字允许在 const 成员函数中修改成员变量
}
private:
mutable int value_;
};

void example_function() {
static int count = 0; // static 关键字使变量 count 存储在程序生命周期内都存在
count++;
}

void example_function(register int num) {
// register 关键字建议编译器将变量 num 存储在寄存器中
// 以提高程序执行速度
// 但是实际上是否会存储在寄存器中由编译器决定
}

在C++中的static除了和C一样的用法之外,在类的定义中还有一些重要的概念,在类中,声明为static的类成员分为两类,静态成员变量,静态成员函数;其中静态成员变量一定要在类外进行初始化。

1.静态成员为所有该类的对象所共有,不属于某个具体的个例

2.静态成员变量要在类外定义,定义时不添加static

3.静态成员函数没有隐藏的this指针,不能访问任何非静态成员

4.访问静态成员变量的特殊方式

5.静态成员和普通的一样,具体三种访问级别,可以具有返回值

static详解:

https://blog.csdn.net/weixin_45031801/article/details/134215425

C++存储类

这个概念其实我并不清楚为什么需要单独拎出来说,因为其实还是这几个关键字,定义了程序中变量和函数的范围和生命周期,存储类说明符一般放在修饰类型的前面,按照现有的资料来说,存储类说明符只是关键字的子集。

存储类:

auto、register、static、extern、mutable、thread_local等

其中最后一个是关键字中没有的,thread_local主要用于具有线程局部存储期的变量,每个线程都有自己的独立副本,线程局部变量和线程的生命周期一致。

auto:声明变量时根据初始化表达式自动推断该变量的类型;声明函数时函数返回值的占位符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>

auto add(int a, int b) -> int {
return a + b; // 返回类型是 int
} //这种语法被称为尾置返回类型

int main() {
std::cout << add(2, 3) << std::endl; // 输出 5
return 0;
}


int x = 10;

// 返回值是值类型
auto byValue() {
return x;
}

// 返回值是引用类型
decltype(auto) byReference() {
return (x); // 加括号返回引用
} //decltype(auto)是另一个形式的占位符

int main() {
byValue() = 20; // 无效,byValue 返回值类型
byReference() = 20; // 有效,byReference 返回引用
std::cout << x << std::endl; // 输出 20
return 0;
}

register:同关键字中一样的用法,不再赘述

static:同关键字

extern:同关键字

mutable:同关键字,适用于需要在const环境中修改状态的特定情况,不是普遍的设计模式。

thread_local:用于多线程环境管理线程特有的变量,每个线程彼此独立。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#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;
}

Tip:由于每个线程都有独立的副本,该类型的变量访问速度相较全局和静态稍慢;且存储类型是静态存储持续时间,在程序运行期间会一直存在。

C++函数

主函数main,其余的常规的函数和C一致,定义函数、函数声明、调用函数等。

函数参数

函数需要使用参数,则必须声明接受参数值的变量,这些被称为形参;形参和函数内部的变量一样,进入函数时创建,退出时销毁,调用函数时,有三种向函数传递参数的方式:

传值调用:该方法把参数的实际值赋值给函数的形式参数,基于此修改函数内的参数对实际的参数没有影响。

指针调用:该方法是将参数的地址赋值给形式参数,在函数内修改形式参数会影响到实际参数。

引用调用:该方法是将参数的引用赋值给形式参数,在函数内修改亦会影响实际参数。

参数列表中的参数我们可以指定其默认值,调用时若是留空,则会使用这个默认值。

Lambda函数与表达式

C++11提供了对匿名函数的支持,也称为Lambda表达式,其把函数看作对象,可以将它们赋值给变量和作为参数进行传递,还可以像函数一样对其求值。

表达式具体形式:[capture] (parameters)->return-type{body}

capture:外部捕获的值

parameters:表达式的参数

return-type:返回值的类型

body:表达式的本体

在Lambda表达式中,可以访问当前作用域的变量,这是C++的闭包行为,C++的闭包行为有传值和传引用的区别,可以使用前面的[capture]来指定:

1
2
3
4
5
6
[]      // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。

闭包是一种捕获外部作用域变量的函数,结合了函数体和变量,允许函数在离开作用域之后仍然可以访问这些变量,捕获的变量和函数体共同组成了对象(闭包),持有变量的方式:值捕获和引用捕获。

捕获变量的生命周期和闭包对象一致,捕获的值在闭包期间有效。引用捕获的变量需要保证原变量在闭包的生命周期内有效,否则会出现未定义行为。

对于值捕获的变量,使用mutable可以使得它在闭包内部可修改,且不影响外部变量。闭包可以将外部变量封装成持久状态,在多次调用中保持状态。

另外还有一点,对于[=]和[&]的形式,lambda表达式可以直接使用this指针,对于[]的形式,要使用this指针,必须显式传入。

1
[this]() { this->someFunc(); }();

捕获this,才可以使用当前对象的成员变量和成员函数,当然捕获this有一些注意事项,和前面类似,主要是生命周期的问题可能会导致悬空引用,从而引发未定义的行为。其次,若捕获的this是常量指针,那么是无法对对象成员进行修改的,若是需要修改,则需要使用[this]或者[&]来进行捕获。

剩下的部分基本就是C语言中相同的内容。