4.1 命名空间(Namaspace)std

namespace诞生的目的:

​ 当你采用不同的模块或程序库是,经常会出现名称冲突现象,这是因为不同的模块和程序库可能对不同对象使用相同的标识符。

namespace特点:

​ 具有扩展开放性,可发生于任何源码文件上。

​ 可以嵌套

void func1()
{
    std::cout << "hello world1" << std::endl;
}

namespace myname
{
    void func1()
    {
        std::cout << "hello world2" << std::endl;
    }
 
    namespace myname1
    {
        void func1()
        {
            std::cout << "hello world3" << std::endl;
        }
    }
}

int main(int argc, char const *argv[])
{
    func1();
    myname::func1();
    myname::myname1::func1();
    return 0;
}

C++标准库中的所有标识符都被定义于一个名为std的namespace中。有三种使用方式:

//1.直接指定标识符
std::cout << "hello world3" << std::endl;

// 使用using declartion,在这句之后不再需要范围限定符std::
using std::cout;
using std::endl;
cout << "hello world3" << endl;

// 使用using dirctive, 让std内所有标识符都有效,这样也不需要写范围限定符std::
using namespace std;
cout << "hello world3" << endl;

4.2 头文件

原因:

头文件的扩展名称,种类花样繁多(.h/.hpp/.hxx)

方式:

在include头文件(C++标准库)的时候,直接去掉其扩展名。同时对于C标准库文件也适用。在前面加上c就行

4.3 差错和异常的处理

C++标准库有不同成分构成,来源不同,设计于实现风格迥异。典型的体现就是差错处理和异常处理

4.3.1 标准的exception class

所有被语言本身或标准库抛出的异常, 都派生自基类exception,定义于<exception>

异常可以分为三组:

1.语言本身支持的异常

2.C++标准库发出的异常

3.程序作用域之外发出的异常。

针对语言支持而设计的异常类

从某种角度来说它们不是标准库的一部分,而是核心语言的一部分

1.运行期间,当一个reference的动态类型转换失败时,dynamic_cast会抛出bad_cast异常,该异常定义于<typeinfo>中

2.运行期类型辨识过程中,如果交给typeid操作符的实参为0或为空指针,typeid操作符会抛出bad_typeid异常,此异常定义于<typeinfo>

3.定义于<exception>内的bad_exception异常用来处理非预期的异常。

针对逻辑差错而设计的异常类

针对逻辑差错而设计的异常总是派生自logic_error。所谓的逻辑差错就是(至少就理论而言)可在程序中避免的错误。C++标准库提供了五种针对逻辑差错的类:

1.invalid_argument表示无效实参

2.length_error指出某个行为“可能超越最大容许大小”

3.out_of_range指出实参值“不在预期范围内”

4.domain_error指出领域范畴(domain)内的错误。

5.自C++11起 future_error用来指出当使用非同步系统调用时发生的逻辑差错。

针对运行期差错而设计的类

派生自runtime_error得异常都用来指出不在程序作用域内且不容易回避的事件

1.range_error指出内部计算时发生区间错误哦。

2.overflow_error指出算术运算发生上溢。

3.underflow_error指出算术运算发生下溢。

4.自C++11起,system_error用来指出因底层操作系统而发生的差错。

5.重要全局操作符new失败,定义于<new>内的bad_alloc就会被抛出,除非用的是nothrow版本。

6.当根据shared_ptr创建weak pointer失败,bad_weak_ptr会抛出

7.当一个function的外覆物被调用但其没有目标是,定义于<function>中的bad_function_call会被抛出

由标准库抛出的异常

几乎所有异常类都由可能被C++标准库抛出

可能间接抛出任何异常(标准库可能用到应用程序开发人员所写代码)

最好只使用标准异常类。

异常类的头文件

#include <exception>    // for classes exception and bac_exception
#include <stdexcept>    // for most logic and runtime error classes
#include <system_error> // for system error(since C++11)
#include <new>          // for out-of-memory exceptions
#include <ios>          // for I/O exceptions
#include <future>       // for errors with async() and futures(since C++11)
#include <typeinfo>     // for bad_cast and bad_typeid

4.3.2 异常类(exception class)的成员

所有标准异常类都提供what()

成员函数what()

对所有标准异常而言,可用来获取“类型以外的附加信息”的唯一成员函数就是what()

差错码:

​ 是一种轻型对象,用来封装差错码值,后者可能由编译器实现指定,有些是标准化的

差错状态:

​ 是一种提供“差错抽象之可移植性抽象层”的对象

对于异常,C++标准库具体给出的有时是差错码,有时是差错状态,取决于上下文和环境

处理差错码和差错状态

C++标准库为差错码和 差错状态提供了两个不同的类型:class std::error_code和class std::error_condition

如果差错处理策略只是检验是否为某种特性的差错码或差错状态,那么差错码和差错状态差别其实不大

为了能处理差错码和差错状态,class std::system_error及其派生出的std::io_base::failure,以及class std::_future_error,都提供了返回一个std::error_code对象的非虚成员函数code()

其它成员

标准异常类中的其它成员,分别负责生成、复制、赋值销毁等操作

唯一具有移植性的异常估算手段,只有打印what()返回的信息

4.3.3 以class exception_ptr传递异常

自C++11起,C++标准库提供一个能力:将异常存储于类型为exception_ptr的对象中,稍后在其它情境下处理

#include <exception>
#include <iostream>
std::exception_ptr eptr;
void foo()
{
    try
    {
        throw 11;
    }
    catch (...)
    {
        eptr = std::current_exception();  // save exception for later processing
    }
}

void bar()
{
    if (eptr != nullptr)
    {
        std::rethrow_exception(eptr);  // process saved exception
    }
}

int main(int argc, char const *argv[])
{
    try
    {
        foo();
        bar();
    }
    catch (int e)
    {
        std::cout << e << std::endl;
    }
    return 0;
}

4.3.4 抛出标准异常

可以在自己的程序库或内部抛出某些标准异常。

不可以抛出基础的exception 异常,也不可以抛出任何针对语言支持而设计的异常:如bad_cast/bad_typeid/bad_exception

为了提供一个error_code对象,标准库提供辅助函数make_error_code(),你只需要给它一个差错码即可。

#include <iostream>
#include <exception>
#include <stdexcept>
void foo()
{
    try
    {
        throw std::out_of_range("out of range (11,123)");
    }
    catch (const std::exception &e)
    {
        std::cout << e.what() << std::endl;
    }
}

void bar()
{
    try
    {
        throw std::system_error(std::make_error_code(std::errc::invalid_argument), "argument ... is not valid.");
    }
    catch (const std::exception &e)
    {
        std::cout << e.what() << '\n';
    }
}

int main(int argc, char const *argv[])
{

    foo();
    bar();

    return 0;
}

4.3.5 自标准异常派生

另一个可在你的程序中运用标准异常类的情况是定义一个直接或间接派生自exception的异常类。

前提条件:

​ what()和code()机制正常运作。

#include <iostream>
#include <exception>
#include <stdexcept>
#include <string_view>

// c11 exception 
// class exception {
// public:
//   exception () noexcept;
//   exception (const exception&) noexcept;
//   exception& operator= (const exception&) noexcept;
//   virtual ~exception();
//   virtual const char* what() const noexcept;
// }

// 构造函数用于传递异常信息,what()返回异常信息就行了(c_string)
class MyException : public std::exception
{
public:
    MyException(int code, std::string msg) : _code(code), _msg(msg) {}
    virtual const char *what() const noexcept override
    {
        std::string content = std::string("Error:") + std::to_string(_code) + std::string("\nerror_msg:") + _msg;
        return content.c_str();
    }

private:
    uint32_t _code;
    std::string _msg;
};

int main(int argc, char const *argv[])
{
    try
    {
        throw MyException(123, "asdfasldjfl");
    }
    catch (const MyException &e)
    {
        std::cout << e.what() << '\n';
    }

    return 0;
}

4.4 callable object(可被调用的对象)

解释:

​ callable object 意思是可被某种方式调用调用其某些函数的对象

它可以是:

​ 一个函数,接受额外传入的arg作为实参

一个指向成员函数的指针 (a pointer to a memeber function),当你通过对象调用它,该对象被传递成为第一个实参(必须是个reference或pointer),其它实参一一对应成员函数的参数。

​ 一个函数对象(function object, 该对象拥有operator() ),附带arg被传递作为实参

一个lambda,严格地说它是一个函数对象

如果想声明callable object,一般而言使用class std::function<>

#include <iostream>
#include <memory>     // shared_ptr
#include <functional> // std::bind
#include <future>     // async
void func(int x, int y)
{
    std::cout << "func x + y = " << x + y << std::endl;
}

auto l = [](int x, int y)
{
    std::cout << "lambda x + y = " << x + y << std::endl;
};

class C
{
public:
    void operator()(int x, int y) const
    {
        std::cout << "class operator() x + y = " << x + y << std::endl;
    }
    void memfunc(int x, int y) const
    {
        std::cout << "class memfunc x + y = " << x + y << std::endl;
    }
};

int main(int argc, char const *argv[])
{
    C c;
    std::shared_ptr<C> sp(new C);

    // bind() uses callable object to  bind arguments:
    std::bind(func, 77, 33)();
    std::bind(l, 77, 33)();
    std::bind(C(), 77, 33)();
    std::bind(&C::memfunc, c, 77, 33)();
    std::bind(&C::memfunc, sp, 77, 33)();

    // async() uses callable objects to start(background) tasks:
    std::async(func, 42, 77);
    std::async(l, 42, 77);
    std::async(c, 42, 77);
    std::async(&C::memfunc, &c, 42, 77);
    std::async(&C::memfunc, sp, 42, 77);

    return 0;
}

// 输出结果
// func x + y = 110
// lambda x + y = 110
// class operator() x + y = 110
// class memfunc x + y = 110
// class memfunc x + y = 110
// func x + y = 119
// lambda x + y = 119
// class operator() x + y = 119
// class memfunc x + y = 119
// class memfunc x + y = 119

4.5 并发与多线程

C++ 11 不论是内核语言或标准库都加强支持并发编程

内核:

​ 保证当你修改被不同的两个线程使用的两个不同对象时,它们彼此独立

​ 引入thread_local,用来定义线程特定的变量和对象

标准库:

​ 若干线程安全相关的保证

​ 支持类和函数的并发运算(启动和同步多个线程)

4.6 分配器

什么是分配器?

C++标准库在许多地方采用特殊对象处理内存的分配和归还。这样的对象就是allocator。

09-22 19:44