# 引用 Reference

引用就是定一个变量的别名,可以理解为给变量取一个外号。

性质:

  • 对引用的操作会改变原变量。
  • 声明时必须初始化,非常量引用必须为左值。
  • 一旦进行初始化,引用的名字不能再传给其它变量。(外号只对应一个人)

实例 - 引用传参:

#include <iostream>
using namespace std;
void fun(int &x) // 此处 x 与 a 的地址相同
{
    x += 2;
}
int main()
{
    int a = 3;
    fun(a);
    /*
    fun (a + 1) 会报错,非常量引用必须为左值
    */
    cout << a << endl; // 输出为 5 
    return 0;
}

实例 - 初始化之后不能传给其它变量:

#include <iostream>
using namespace std;
int main()
{
    int a = 2, b = 3;
    int& r = a;
    r = b;
    cout << r << ' ' << a << endl; // 输出 3 3
    return 0;
}

# 空指针与动态内存分配

空指针:

  • 为解决 0 带来的二义性问题,C++11 中引入保留字 nullptr 作为空指针。

动态内存分配:

  • 通过 new 运算符申请动态内存

    • new 类型 (初始值)
    • new 类型 [数量]
  • 通过 delete 运算符释放内存

    • delete 指针

实例:

#include <iostream>
using namespace std;
int main()
{
    int* p = nullptr;
    int* q{nullptr};
    p = new int(233);
    q = new int[4];
    cout << *p << endl; // 233
    *(q + 1) = *p;
    cout << q[1] << endl; // 233
    delete p;
    return 0;
}

# 列表初始化

C++ 逐渐 python 化(抱头蹲防

C++11 标准之前的初始化方法:

int x = 0;
int y(2);
int arr[] = {2, 3, 3};
char s[] = "Hello";

C++11 新特性:

// 直接列表初始化
int x{}; // 0
int y{1}; // 1
int arr1[]{2, 3, 3}; // 2, 3, 3
char s1[4]{'q', 'w', 'q'}; // "qwq\0" 多余的初始化为 0,对比 s3
char s2[]{"qwq"}; // "qwq\0"
// 拷贝列表初始化
int x = {}; // 同上,加上等号,一般情况下等价使用
char s3[]  {'a', 'b'}; // "ab"
char s4[] = "ab"; // "ab\0"

尽量使用列表初始化,除非你有个很好的不用它的理由。

列表初始化不允许丢失数据精度的隐式类型转换。

eg: int x{1.1} 编译器报错。

# 类型转换

  • 隐式类型转换
  • C 风格的强制类型转换: (Type) Value
  • C++ 风格的强制类型转换: static_cast<Type>(Value)

实例:

cout << static_cast<double>(1) / 2 << endl; // 0.5
cout << static_cast<double>(1 / 2) << endl; // 0

RTFM : Runoob_C++ 强制转换运算符 懒得看了,以后有机会回来补,溜了,,

# 自动类型推导:auto 与 decltype

# auto 关键字

C++03 及之前的标准种,auto 放在变量声明之前,声明变量的存储策略。但是这个关键字常省略不写。

C++11 中,auto 关键字放在变量之前,作用是在声明变量的时候 根据变量初始值的类型 自动为此变量选择匹配的类型。

使用限制:

  • auto 变量必须在定义时初始化,这类似于 const 关键字

    auto a = 10; // 正确 
    auto b; // 无法推断,报错
    b = 10;
  • 定义在一个 auto 序列的变量必须始终推导成同一类型

    auto a = 2, b{3};
    auto c = 4, d{'c'};
  • 去除引用与 const,添加 '&' 可保留原类型

    int a{10};
    int& b = a;
    auto c = b;
    c = 5;
    cout << a << ' ' << b << ' ' << c << endl; // 10, 10, 5
    int a{10};
    int& b = a;
    auto& c = b;
    c = 5;
    cout << a << ' ' << b << ' ' << c << endl; // 5, 5, 5
    const int a{10};
    auto& c = a;
    c = 5; // 报错
  • 初始化为数组推断为指针,添加 '&' 可保留数组

    int a[]{2, 3, 3};
    auto b = a;
    auto& c = a;
    cout << typeid(b).name() << ' ' << typeid(c).name() << endl; //Pi, A3_i

    此处注意:

    int a[]{2, 3, 3};
    auto& c = a; // 虽然 c 类型是数组,但其仍然是引用 a
    c[2] = 1;
    cout << c[2] << ' ' << a[2] << endl; // 1, 1
  • C++14 中,auto 可以作为函数的返回值类型和参数类型

Almost Alawys Auto: 尽可能使用 auto

一些例子:

auto x = 42;
auto x = int{42};
auto x = 42.f;
auto x = 42ul;
auto x = "42"s; // 字符串,C++14
auto f(double) -> int; // 声明函数
auto f(double) {...};

数组的初始化在运行期间完成,而 auto 是在编译期间完成,eg:

auto x[] = {1, 2, 3}; // 编译器报错

# decltype 关键字

发音:declare_type

decltype 是在编译期推导一个表达式的类型,它只做静态分析,因此它不会导致已知类型表达式执行。

decltype 主要用于泛型编程(模板),所以现在先跑路了

下篇:[C++ 笔记] C 语法扩展 II

# 异常处理

# 语法

三个关键字 try, throw, catch

语法:

try
{
    // 随便写点啥,这里可能会用 trow 抛异常,假设在某处
    throw exception;
}
catch (exceptionType exceptionName) // 会获取上面抛出对应类型,并开始执行这里
{
    // 随便干啥 QwQ
    // 执行完毕后直接退出整个 try...catch 语句块
    // 对于上一句,需要注意的是,继承关系中,应该先 catch 子类再 catch 父类,不然 catch 子类就没意义了 emm
}
catch (exceptionType2 exceptionName2) // 若执行了上面的 catch, 这边直接跳过
{
	// 随便干啥 QwQ
}
catch (...) //catch 所有异常
{
    // 随便干啥 QwQ
}

throw 抛出异常却没有 catch 来接的话程序就直接炸了 qwq

实例:

#include <iostream>
using namespace std;
double division(int a, int b)
{
  if (b == 0)
  {
    throw "Division by zero condition!";
  }
  return (a / b);
}
int main()
{
  int x = 50;
  int y = 0;
  double z = 0;
  try
  {
    z = division(x, y); // 执行这里时,因为 b==0 抛出异常
    cout << z << endl;
  }
  catch (const char *msg) //msg 接收字符串类型异常 "Division by zero condition!"
  {
    cerr << msg << endl;
  }
  return 0;
}

# 一些神奇的

  • 异常机制使得没有返回值的构造函数可以向程序报告错误。

实例:

#include <iostream>
using namespace std;
class division
{
private:
	double ans;
public:
	division(double a = 1, double b = 1)
	{
		if (b == 0)
		{
			throw "Division by zero condition!";
		}
		this -> ans = a / b;
	}
	double getAns()
	{
		return this -> ans;
	}
};
int main()
{
  try
  {
    division* d = new division{233, 0};
    cout << d -> getAns() << endl;
  }
  catch (const char *msg)
  {
    cerr << msg << endl; //Division by zero condition!
  }
  return 0;
}
  • 重复抛出异常,处理一半再抛出去。

实例:

#include <iostream>
using namespace std;
int main()
{
	try
	{
		try
		{
			try
			{
				throw 233;
			}
			catch (int &a)
			{
				cerr << a << endl; // 233
				a += 233;
				throw; // 把 &a 再扔出去 qwq
			}
		}
		catch (int a) // 注意这里是个形参
		{
			cerr << a << endl; // 466
			a += 233;
			throw;
		}
	}
	catch (int a)
	{
		cerr << a << endl; // 466
	}
	return 0;
}
更新于