• C++点滴


    noexcept 异常说明

    void recoup(int) noexcept;  //recoup 不会抛出异常

    void recoup(int) noexcept(true);

    void recoup(int) throw();  //早期版本

    void alloc(int);    //可能抛出异常

    void alloc(int) noexcept(false);

    C++11新标准中,可以通过提供noexcept说明指定某个函数不会抛出异常。

    作用:知道函数不会抛出异常有助于简化调用该函数的代码;如果编译器确认函数不会抛出异常,它就能执行某些特殊的优化操作,而这些优化操作并不适用于可能出错的代码。

    两种使用情形:确认函数不会不会抛出异常;根本不知道如何处理异常。

    一旦一个noexcept函数抛出了异常,程序就会调用terminate以确保遵守不在运行时抛出异常的承诺。

    noexcept运算符:

    noexcept(e) 如果e调用的所有函数都做了不抛出说明且e本身也不含有throw语句时,表达式为true;否则noexcept(e)返回false

    void f() noexcept( noexcept(g()));   //f g的异常说明一致

    函数指针及该指针所指的函数必须具有一致的异常说明。

    void (*pf1)(int) noexcept = recoup; //OK

    void (*pf2)(int) = recoup; //OK,recoup不会抛出异常,pf2可能抛出异常,二者之间互不干扰

    pf1 = alloc;  //错误

    pf2 = alloc;  //OK

    explicit

    抑制构造函数定义的隐式转换

    class Sales_data{

    Sales_data()=default;

    explicit Sales_data(std::istream&);

    }

    通过将构造函数声明为explicit,阻止隐式转换。在类内声明构造函数时使用explicit关键字,在类外定时时不应重复。

    item.combine(cin); //错误,istream构造函数是explicit

    Sales_data item(cin); //explicit构造函数只能用于直接初始化

    显示的类型转换运算符(C++11

    class SmallInt{

    public:

         explicit operator int() const { return val;}

         //...

    };

    SmallInt si = 3; //OK

    si + 3;  //ERROR,此处需要隐式的类型转换,但类的运算符是显示的

    static_cast<int>(si)  + 3;  //OK

    incomplete type

    C++的类可以进行前向声明。但是,仅仅进行前向声明而没有定义的类是不完整的,这样的类,只能用于定义指针、引用、以及用于函数形参的指针和引用。

    而不能定义对象(因为此时编译器只知道这是个类,还不知道这个类的大小有多大),也不能访问类的对象,仍和形式的访问都允许(因为此时根本不知道有些什么成员)。等到类正式定义以后,就可以以各种方式使用该类了。

    nontrivial

    trivial意思是无意义,这个trivialnon-trivial是对类的四种函数来说的:

    默认构造函数(default constructor)

    拷贝构造函数(copy constructor)

    赋值函数(copy assignment operator)

    析构函数(destructor)

    如果至少满足下面3条里的一条:

    显式(explict)定义了这四种函数

    类里有非静态非POD的数据成员

    有基类。

    那么上面的四种函数是non-trivial函数,比如叫non-trivial constructornon-trivial copy constructor…,也就是说有意义的函数,里面有以下必要的操作,比如类成员的初始化,释放内存等

    std::remove

    Return: An iterator to the element that follows the last element not removed.

    The range between first and this iterator includes all the elements in the sequence that do not compare equal to val.

    The relative order of the elements not removed is preserved, while the elements between the returned iterator and last are left in a valid but unspecified state.

    // remove algorithm example

    #include <iostream>     // std::cout

    #include <algorithm>    // std::remove

    int main () {

      int myints[] = {10,20,30,30,20,10,10,20};      // 10 20 30 30 20 10 10 20

      // bounds of range:

      int* pbegin = myints;                       // ^

      int* pend = myints+sizeof(myints)/sizeof(int);   //                       ^

      pend = std::remove (pbegin, pend, 20);         // 10 30 30 10 10 ?  ?  ?

                                              // ^            ^

      std::cout << "range contains:";

      for (int* p=pbegin; p!=pend; ++p)

        std::cout << ' ' << *p;

      std::cout << ' ';

      return 0;

    }

    下面示例不安全:

    #include <algorithm>

    #include <iostream>

    #include <vector>

    void f(std::vector<int> &c) {

    c.erase(std::remove(c.begin(), c.end(), 42), c.end());

    for (auto v : c) {

    std::cout << "Container element: " << v << std::endl;

    }

    }

    安全做法一:

    #include <algorithm>

    #include <iostream>

    #include <vector>

    void f(std::vector<int> &c) {

    auto e = std::remove(c.begin(), c.end(), 42);

    for (auto i = c.begin(); i != c.end(); i++) {

    if (i < e) {

    std::cout << *i << std::endl;

    }

    }

    }

    安全做法二:erase unspecified state element.

    std::erase  

    #include <algorithm>

    #include <iostream>

    #include <vector>

    void f(std::vector<int> &c) {

    c.erase(std::remove(c.begin(), c.end(), 42), c.end());

    for (auto v : c) {

    std::cout << "Container element: " << v << std::endl;

    }

    }

    std::vector::at()

    Position of an element in the container.

    If this is greater than, or equal to, the vector size, an exception of type out_of_range is thrown.

    Notice that the first element has a position of 0 (not 1).

    #include <stdexcept>

    #include <string>

    extern std::size_t get_index();

    void f() {

    std::string s("01234567");

    try {

    s.at(get_index()) = '1';

    } catch (std::out_of_range &) {

    // Handle error

    }

    }

    std::tie

    https://en.cppreference.com/w/cpp/utility/tuple/tie

    #include <iostream>

    #include <string>

    #include <set>

    #include <tuple>

    struct S {

        int n;

        std::string s;

        float d;

        bool operator<(const S& rhs) const

        {

            // compares n to rhs.n,

            // then s to rhs.s,

            // then d to rhs.d

            return std::tie(n, s, d) < std::tie(rhs.n, rhs.s, rhs.d);

        }

    };

    int main()

    {

        std::set<S> set_of_s; // S is LessThanComparable

        S value{42, "Test", 3.14};

        std::set<S>::iterator iter;

        bool inserted;

        // unpacks the return value of insert into iter and inserted

        std::tie(iter, inserted) = set_of_s.insert(value);

        if (inserted)

            std::cout << "Value was inserted successfully ";

    }

    std::tuple

    https://en.cppreference.com/w/cpp/utility/tuple

    #include <tuple>

    #include <iostream>

    #include <string>

    #include <stdexcept>

    std::tuple<double, char, std::string> get_student(int id)

    {

        if (id == 0) return std::make_tuple(3.8, 'A', "Lisa Simpson");

        if (id == 1) return std::make_tuple(2.9, 'C', "Milhouse Van Houten");

        if (id == 2) return std::make_tuple(1.7, 'D', "Ralph Wiggum");

        throw std::invalid_argument("id");

    }

    int main()

    {

        auto student0 = get_student(0);

        std::cout << "ID: 0, "

                  << "GPA: " << std::get<0>(student0) << ", "

                  << "grade: " << std::get<1>(student0) << ", "

                  << "name: " << std::get<2>(student0) << ' ';

        double gpa1;

        char grade1;

        std::string name1;

        std::tie(gpa1, grade1, name1) = get_student(1);

        std::cout << "ID: 1, "

                  << "GPA: " << gpa1 << ", "

                  << "grade: " << grade1 << ", "

                  << "name: " << name1 << ' ';

        // C++17 structured binding:

        auto [ gpa2, grade2, name2 ] = get_student(2);

        std::cout << "ID: 2, "

                  << "GPA: " << gpa2 << ", "

                  << "grade: " << grade2 << ", "

                  << "name: " << name2 << ' ';

    }

    extern C

    链接指示,C++程序有时需要调用其他语言编写的函数,最常见的是C语言编写的函数。其他语言中的函数也需要进行声明,并且该声明必须指定返回类型和形参列表。

    声明一个非C++的函数:

    extern “C” size_t strlen(const char*);

    或者

    extern “C”{

    int strcmp(const char*, const char*);

    char *strcat(char*, const char*);

    }

    extern “C”{

     #include <string.h> //头文件中所有的普通函数声明都被认为是有链接指示的语言编写的

    }

    指向extern C函数的指针:

    extern “C” void (*pf)(int);  //pf指向一个C函数,该函数接受一个int,返回void

    void (*pf1)(int);   //指向一个C++函数

    pf = pf1;   //错误:pfpf1的类型不同

    extern “C” void f1(void(*)(int));  //f1是一个C函数,它的形参是指向一个C函数的指针

    extern “C” typedef void FC(int);

    void f2(FC *);  //f2是一个C++函数,该函数形参是指向C函数的指针

    导出C++函数到其他语言:
    extern “C” double calc(double dparm){/**/} //calc函数可以被C程序调用

    #ifdef __cplusplus  //编译预处理,同一个文件即可以按照C编译,也可以按照C++编译

    extern “C”

    #endif

    int strcmp(const char*, const char*);

    unique_ptr  

    某个时刻只能有一个unique_ptr指向一个给定的对象。当unique_ptr销毁时,它指向的对象也被销毁。当定义一个unique_ptr时,需要将它绑定到一个new返回的指针上。

    unique_ptr<T> u1

    空unique_ptr,可以指向类型为T的对象。u1会使用delete来释放它的指针

    unique_ptr<T, D> u2

    u2会使用一个类型为D的可调用对象来释放它的指针

    unique_ptr<T, D> u(d)

    用类型为D的对象d代替delete

    u = nullptr

    释放u指向的对象,将u置为空

    u.release()

    u放弃对指针的控制权,返回指针,并将u置为空。调用release会切断unique_ptr和它原理管理的对象的联系。release返回的指针一般用来初始化另一个智能指针或给另一个智能指针赋值。

    u.reset()

    释放u指向的对象

    u.reset(q)

    释放u指向的对象,将u置为q

    u.reset(nullptr)

    new delete重载

    全局作用域中定义operator new/delete

    成员函数定义operator new/delete

    当编译器发现一条new表达式,将在程序中查找可调用的operator函数。如果被分配的对象是类对象,则首先在类中找是否有operator成员,如果没有,再从全局作用域中查找。

    类也可以用作用域运算符命令::new表达式已忽略定义在类中的函数。

    placement new()

    int *p1 = new int;   //如果分配失败,new抛出std::bad_alloc

    int *p2 = new (nothrow) int;  //如果分配失败,new返回一个空指针

    定位new表达式,控制内存分配

    原文:https://www.cnblogs.com/xzlq/p/9504851.html

    所谓placement new就是在用户指定的内存位置上构建新的对象,这个构建过程不需要额外分配内存,只需要调用对象的构造函数即可。

    举例来说:

    class foo{};

    foo* pfoo = new foo;

    pfoo指向的对象的地址你是不能决定的,因为new已经为你做了这些工作。第一步分配内存,第二步调用类的构造函数。

    placement new是怎么做的呢,说白了就是把原本new做的两步工作分开来。第一步你自己分配内存,第二步你调用类的构造函数在自己分配的内存上构建新的对象。

    placement new的好处:

    1)在已分配好的内存上进行对象的构建,构建速度快。

    2)已分配好的内存可以反复利用,有效的避免内存碎片问题。

    class Foo

    {

        char cc;

        float f;

    public:

        void print() { std::cout << "ADDR: " << this << std::endl; }

        void set_f( float _f ) { std::cout << "set f val : " << _f << std::endl; f = _f; }

        void get_f() { std::cout << "get f val : " << f << std::endl; }

    };

    1)分配内存

    char* buff = new char[ sizeof(Foo) * N ];

    memset( buff, 0, sizeof(Foo)*N );

    2)构建对象

    Foo* pfoo = new (buff)Foo;

    3)使用对象

    pfoo->print();

    pfoo->set_f(1.0f);

    pfoo->get_f();

    4)析构对象,显式的调用类的析构函数。

    pfoo->~Foo();

    5)销毁内存

    delete [] buff;

    上面5个步骤是标准的placement new的使用方法。

    对于buff这块内存可以反复使用,只要重复2)、3)、4)步骤即可。

    placement new还可以解决的一个问题是建立带参数的构造函数对象数组。

    代码示例如下:

    class CPong

    {

    public:

        CPong( int m ) : v(m) { std::cout << "CPong ctor." << std::endl; }

    private:

        int v;

    };

    char* pong = new char[ sizeof(CPong) * 10 ];

    CPong* pp = (CPong*)pong;

    for ( int i=0; i<10; ++i )

    {

         new (pp+i)CPong(i);

    }

    for ( int j=0; j<10; ++j )

    {

        pp[j].~CPong();

    }

    delete [] pong;

    虚函数、虚表指针 

    移动对象

    exception

    #include <exception>

    #include <iostream>

    struct S : std::exception {

    const char *what() const noexcept override {

    return "My custom exception";

    }

    };

    void f() {

    try {

    throw S();

    } catch (std::exception &e) {

    std::cout << e.what() << std::endl;

    }

    }

    class B {};

    class D : public B {};

    void f() {

    try {

    // ...

    } catch (D &d) {

    // ...

    } catch (B &b) {

    // ...

    }

    }

  • 相关阅读:
    Java对【JSON数据的解析】--Gson解析法
    Java对【JSON数据的解析】--官方解析法
    Java之JSON数据
    网络编程应用:基于UDP协议【实现聊天程序】--练习
    {网络编程}和{多线程}应用:基于UDP协议【实现多发送方发送数据到同一个接收者】--练习
    PHP获取页面执行时间的方法(推荐)
    Linux下查看CPU型号,内存大小,硬盘空间的命令(详解)
    Elasticsearch 全字段搜索_all,query_string查询,不进行分词
    mysql 查询某字段值全是数字
    linux服务器中Apache隐藏index.php失败
  • 原文地址:https://www.cnblogs.com/sunnypoem/p/12189111.html
Copyright © 2020-2023  润新知