【深蓝学院C++】第14章 其它的工具与技术 笔记

1.异常处理

(1)用于处理程序在调用过程中的非正常行为

​ 1 传统的处理方法:传返回值表示函数调用是否正常结束

​ 2 C++ 中的处理方法:通过关键字 try/catch/throw 引入异常处理机制

(2)异常触发时的系统行为:栈展开

​ 1 抛出异常后续的代码不会被执行

​ 2 局部对象会按照构造相反的顺序自动销毁

​ 3 系统尝试匹配相应的 catch 代码段

​ 如果匹配则执行其中的逻辑,之后执行 catch 后续的代码

​ 如果不匹配则继续进行栈展开,直到 跳出 main 函数,触发 terminate 结束运行

#include <iostream>
void f1()
{
    throw 1;
}
void f2()
{
    int x2;
    try
    {
        f1();
    }
    catch (int)
    {
        std::cout << "exception is catched in f2\n";
    }
    std::cout << "other logic in f2\n";
}
void f3()
{
    f2();
}
int main()
{
    try
    {
        f3();
    }
    catch (int)
    {
        std::cout << "exception is catched in main\n";
    }
}

(3)异常对象

​ 1 系统会使用抛出的异常拷贝初始化一个临时对象,称为异常对象

​ 2 异常对象会在栈展开过程中被保留,并最终传递给匹配的 catch 语句

(4)try / catch 语句块

​ 1 一个 try 语句块后面可以跟一到多个 catch 语句块

​ 2 每个 catch 语句块用于匹配一种类型的异常对象

​ 3 catch 语句块的匹配按照从上到下进行

​ 4 使用 catch(…) 匹配任意异常

​ 5 在 catch 中调用 throw 继续抛出相同的异常

(5)在一个异常未处理完成时抛出新的异常会导致程序崩溃

​ 1 不要在析构函数或 operator delete 函数重载版本中抛出异常

​ 2 通常来说, catch 所接收的异常类型为引用类型

(6)异常与构造、析构函数

​ 1 使用 function-try-block保护初始化逻辑

#include <iostream>
struct Str
{
    Str()
    {
        throw 100;
    }
};
class Cla
{
public:
    Cla()
    try : m_mem()
    {
    }
    catch (int)
    {
        std::cout << "Exception is called at Cla::Cla." << std::endl;
    }
private:
    Str m_mem;
};
int main()
{
    try
    {
        Cla obj;
    }
    catch (int)
    {
        std::cout << "Exception is called at main." << std::endl;
    }
}
#include <iostream>
struct Str
{
    Str()
    {
        throw 100;
    }
};
class Cla
{
public:
    Cla()
    try : m_mem()
    {
    }
    catch (int)
    {
        std::cout << "Exception is called at Cla::Cla." << std::endl;
    }
private:
    Str m_mem;
};

void fun(Str x)
try
{

}
catch (...)
{
    std::cout << 123 << std::endl;
}

int main()
{
    try
    {
        fun(Str());
    }
    catch (int)
    {
        std::cout << 456 << std::endl;
    }
}
#include <iostream>

void fun()
try
{
    throw 12;
}
catch (...)
{
    std::cout << 123 << std::endl;
}

int main()
{
    fun();
}

​ 2 在构造函数中抛出异常:

​ 已经构造的成员会被销毁,但类本身的析构函数不会被调用

(7)描述函数是否会抛出异常

​ 1 如果函数不会抛出异常,则应表明以为系统提供更多的优化空间

​ C++ 98 的方式: throw() / throw(int, char) --编译期

​ C++11 后的改进: noexcept / noexcept(false) --运行期

​ 2 noexcept

​ 限定符:接收 false / true 表示是否会抛出异常

​ 操作符:接收一个表达式,根据表达式是否可能抛出异常返回 false/true

​ 在声明了 noexcept 的函数中抛出异常会导致 terminate 被调用,程序终止

​ 不作为函数重载依据,但函数指针、虚拟函数重写时要保持形式兼容

#include <iostream>
void fun() //noexcept
{

}

int main()
{
    std::cout << noexcept(fun()) << std::endl;
}
#include <iostream>
void fun() noexcept
{

}
int main()
{
    void (*ptr)() noexcept = fun;
    (*ptr)();
}
#include <iostream>
class Base
{
public:
    virtual void fun() noexcept(false)
    {}
};
class Derive : public Base
{
public:
    void fun() noexcept override
    {}
};
int main()
{
    Derive d;
    Base& ref = d;
    ref.fun();
}

(8)标准异常

(9)正确对待异常处理

​ 不要滥用:异常的执行成本非常高

​ 不要不用:对于真正的异常场景,异常处理是相对高效、简洁的处理方式

​ 编写异常安全的代码

#include <iostream>
#include <stdexcept>
#include <memory>
void fun(int x)
{
    std::unique_ptr<int> ptr = std::make_unique<int>(3);
    throw 1234;
}
int mian()
{
    try
    {
        fun(100);
    }
    catch (int& e)
    {
        std::cout << e << std::endl;
    }
}

2.枚举与联合

(1)枚举(enum) :一种取值受限的特殊类型

​ 1 分为无作用域枚举与有作用域枚举( C++11 起)两种

#include <iostream>
enum Color
{
    Red, 
    Green, 
    Yellow
};
int main()
{
    Color x = Red;
}
#include <iostream>
enum class Color
{
    Red, 
    Green, 
    Yellow
};
int main()
{
    Color x = Color::Red;
}

​ 2 枚举项缺省使用 0 初始化,依次递增,可以使用常量表达式来修改缺省值

#include <iostream>
enum class Color
{
    Red, 
    Green, 
    Yellow
};
int main()
{
    Color x = Color::Red;
    std::cout << x << std::endl;
    Color y = Color::Green;
    std::cout << y << std::endl;
}

​ 3 可以为枚举指定底层类型,表明了枚举项的尺寸

#include <iostream>
enum Color : char
{
    Red, 
    Green, 
    Yellow
};
int main()
{
	std::cout << sizeof(Color) << std::endl;
}

​ 4 无作用域枚举项可隐式转换为整数值;也可用 static_cast 在枚举项与整数值间转换

#include <iostream>
enum class Color
{
    Red, 
    Green, 
    Yellow
};
void fun(Color x)
{
    
}
int main()
{
	fun(static_cast<Color>(100));
    std::cout << static_cast<int>(Color::red) << std::endl;
}	

​ 5 注意区分枚举的定义与声明

#include <iostream>
enum cColor;
int main()
{
    Color x;
}	
#include <iostream>
enum Color : int;
int main()
{
    Color x;
}	

(2)联合( union ):将多个类型合并到一起以节省空间

#include <iostream>
union Str
{
    int x;
    int y;
};
int main()
{
    std::cout << sizeof(Str) << std::endl;
    Str obj;
    obj.x = 100;
    std::cout << obj.y << std::endl;
}

​ 1 通常与枚举一起使用

#include <iostream>
enum Type
{
    Char,
    In
};
union Str
{
    char x;
    int y;
};
struct S{
    Type t;
    Str obj;
};
int main()
{
    std::cout << sizeof(Str) << std::endl;

    S s;
    s.t = Char;
    s.obj.x = 'c';
}

​ 2 匿名联合

#include <iostream>
struct S{
    enum Type
	{
    	Char,
    	In
	};
	union 
	{
   		char x;
    	int y;
	};
    Type t;
};
int main()
{
    std::cout << sizeof(Str) << std::endl;

    S s;
    s.t = S::Char;
    s.x = 'c';
}

​ 3 在联合中包含非内建类型( C++11 起)

3.嵌套类与局部类

(1)嵌套类:在类中定义的类

​ 1 嵌套类具有自己的域,与外围类的域形成嵌套关系

​ 嵌套类中的名称查找失败时会在其外围类中继续查找

​ 2 嵌套类与外围类单独拥有各自的成员

#include <iostream>
class Out
{
    using MyInt = int;
    inline static int val2 = 4;
public:
    class In
    {
    public:
        inline static int val = val2;
    };
};
int main()
{
    Out::In::val;
}

(2)局部类:可以在函数内部定义的类

​ 1 可以访问外围函数中定义的类型声明、静态对象与枚举

​ 2 局部类可以定义成员函数,但成员函数的定义必须位于类内部

​ 3 局部类不能定义静态数据成员

#include <iostream>
void fun()
{
    using MyInt = int;
    struct Helper
    {
        MyInt x;
        int y;
        int inc()
        {
            return x++;
        }
    };
    Helper h;
    h.inc();
}
int main()
{
    fun();
}

4.嵌套名字空间与匿名名字空间

(1)嵌套名字空间

​ 1 名字空间可以嵌套,嵌套名字空间形成嵌套域

​ 2 注意同样的名字空间定义可以出现在程序多处,以向同一个名字空间中增加声明或定义

​ 3 C++17 开始可以简化嵌套名字空间的定义

#include <iostream>
namespace Out
{
    int y;
}
namespace Out::In
{
    int z;
}
int main()
{
    Out::In::z;
}

(2)匿名名字空间

​ 1 用于构造仅翻译单元可见的对象

​ 2 可用 static 代替

​ 3 可作为嵌套名字空间

#include <iostream>
namespace
{
    int y;
}

int main()
{

}
#include <iostream>
namespace MyNs
{
    namespace
    {
        int y;
    }
    
}

int main()
{
    MyNs::y = 3;
}

5.位域与 volatile 关键字

(1)位域:显示表明对象尺寸(所占位数)

​ 1 在结构体 / 类中使用

​ 2 多个位域对象可能会被打包存取

​ 3 声明了位域的对象无法取地址,因此不能使用指针或非常量引用进行绑定

​ 4 尺寸通常会小于对象类型所对应的尺寸,否则取值受类型限制

#include <iostream>
struct A
{
    bool b1 : 1;
    bool b2 : 1;
};

int main()
{
    std::cout << sizeof (A) << std::endl;
}
#include <iostream>
struct A
{
	char b : 1000;
};

int main()
{
    std::cout << sizeof (A) << std::endl;
}

(2)volatile 关键字

​ 1 表明一个对象的可能会被当前程序以外的逻辑修改

​ 2 相应对象的读写可能会加重程序负担

​ 3 注意慎重使用 一些情况下可以用 atomic 代替

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值