[c++] c++ 中的一些关键字:explicit, delete, default, override, final, noexcept

1 explicit

explicit 的意思是清楚的,明显的。一般用在类的构造函数中,防止隐式转换。

explicit specifier - cppreference.com

如下代码,

(1) 类 A 的两个构造函数都没有使用 explicit 修饰,所以如下两行代码,隐式转换,是允许的  

A a1 = 1;
A a4 = {4, 5};

(2) 类 B 的两个构造函数都使用 explicit 修饰了,不允许隐式构造,所以下边两行代码编译不通过

B b1 = 1;
B b4 = {4, 5};

(3) 类 B,如下代码,使用强制类型转换,是可以的。

B b5 = (B)1;

#include <iostream>
#include <string>

class A
{
public:
    A(int) { }      // converting constructor
    A(int, int) { } // converting constructor (C++11)
};

class B
{
public:
    explicit B(int) { }
    explicit B(int, int) { }
};

int main()
{
    A a1 = 1;      // OK: copy-initialization selects A::A(int)
    A a2(2);       // OK: direct-initialization selects A::A(int)
    A a3 {4, 5};   // OK: direct-list-initialization selects A::A(int, int)
    A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
    A a5 = (A)1;   // OK: explicit cast performs static_cast

    // B b1 = 1;      // error: copy-initialization does not consider B::B(int)
    B b2(2);       // OK: direct-initialization selects B::B(int)
    B b3(4, 5);   // OK: direct-list-initialization selects B::B(int, int)
    // B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int, int)
    B b5 = (B)1;   // OK: explicit cast performs static_cast
    return 0;
}

2 delete

指定某个函数禁用。

在单例模式中,一个类只允许创建一个对象,同时也不允许这个类进行拷贝构造或者对象之间进行赋值。怎么做到不让类进行拷贝构造,以及不让对象之间进行赋值呢,这个时候就可以使用 delete 来修饰拷贝构造函数和赋值运算符。

如果在代码中使用了禁用的函数,那么编译的时候会报错。

#include <iostream>
#include <mutex>
 
class Test {
public:
  static Test *GetInstance() {
    std::lock_guard<std::mutex> lock(mtx);
    if (instance == nullptr) {
      instance = new Test();
      return instance;
    }
    return instance;
  };
 
  Test(const Test &) = delete;
  Test &operator=(const Test &) = delete;
 
  ~Test() {
    std::cout << "~Test()" << std::endl;
  };
 
  class Recycler {
  public:
    ~Recycler() {
      if (Test::instance) {
        delete Test::instance;
      } else {
        std::cout << "no need to recycle" << std::endl;
      }
    }
  };
  static Recycler recycler;
 
  void Do() {
    std::cout << "Do()" << std::endl;
  }
 
private:
  static Test *instance;
  static std::mutex mtx;
  Test() {
    std::cout << "Test()" << std::endl;
  };
};
 
Test *Test::instance = nullptr;
std::mutex Test::mtx;
Test::Recycler recycler;
 
void TestDo(Test test) {
  test.Do();
}
 
int main() {
  Test *test = Test::GetInstance();
  test->Do();
  return 0;
}

3 default

这个关键字和 c++ 编译器默认生成的函数有关系。如果定义了一个空类,那么 c++ 编译器会默认生成构造函数,析构函数,拷贝构造函数,拷贝赋值运算符,取地址运算符,取值运算符,移动拷贝构造函数,移动赋值运算符。

以构造函数为例,如果我们自己没有定义构造函数,那么编译器会默认生成一个无形参的构造函数;如果我们定义了有形参的构造函数,那么编译器就不会生成默认无形参的构造函数了。而默认的构造函数在某些使用场景下也会用到,这个时候我们就不需要自己定义一个这样的构造函数,而是使用 default 关键字来提醒编译器,虽然我自己写了有参数的构造函数,但是也让编译器生成默认构造函数。

如下代码,如果不将 Test() 声明为 default,那么在 main() 函数中的 Test t1 这行代码便会导致编译错误。

#include <iostream>
#include <string>

class Test {
public:
  Test() = default;
  Test(std::string str) {
    std::cout << "Test(), str = " << str << std::endl;;
  }
};

int main() {
  Test t1;
  Test t2("hello");
  return 0;
}

自己定义的构造函数需要和 default 构造函数形成重载,不能定义一个和 default 构造函数一样的构造函数,这样是不允许的,比如下边的代码。

#include <iostream>
#include <string>

class Test {
public:
  Test() = default;
  Test() {
    std::cout << "Test()" << std::endl;;
  }
};

int main() {
  Test t1;
  return 0;
}

4 override

显式指定,覆写。

(1)override 可以显式的说明函数覆盖了基类中的虚函数,增加了可读性。对于虚函数来说,即使不使用 override 指定,子类也可以覆写基类中的虚函数

(2)对于非虚函数来说,派生类不能覆写父类的同名函数,使用 override 编译时会报错

#include <iostream>
#include <string>

class Base {
public:
  virtual void Do() {
    std::cout << "Base() Do()\n";
  }

  void Do1() {
    std::cout << "Base() Do1()\n";
  }
};

class Derived : public Base {
public:
  virtual void Do() override {
    std::cout << "Derived() Do()\n";
  }

  // 非虚函数,不能使用 override
  // 派生类也无法覆写父类的同名函数
  void Do1() /* override */ {
    std::cout << "Derived() Do1()\n";
  }
};

int main() {
  Base *b = new Derived;
  b->Do();
  b->Do1();
  return 0;
}

5 final

(1)修饰类,说明这个类不能被继承

(2)非虚函数,不能使用 final 来修饰。因为非虚函数,本身就具有 final 的性质,派生类不能覆写基类中的非虚函数

(3)修饰虚函数,派生类中不能覆写

#include <iostream>
#include <string>

class Base {
public:
  virtual void Do1() {
    std::cout << "Base() Do1()\n";
  }

  virtual void Do2() final {
    std::cout << "Base() Do2()\n";
  }
};

class Derived1 final : public Base {
public:
  virtual void Do1() override {
    std::cout << "Derived1() Do1()\n";
  }

  virtual void Do2() {
    std::cout << "Derived1() Do2()\n";
  }
};

class Derived2 : public Derived1 {
public:
  virtual void Do1() {
    std::cout << "Derived2() Do1()\n";
  }

  virtual void Do2() final {
    std::cout << "Derived2() Do2()\n";
  }
};

int main() {
  Base *b1 = new Derived1;
  Base *b2 = new Derived2;
  b1->Do1();
  b2->Do1();
  return 0;
}

编译报错:

6 noexcept

这个函数不会抛异常,当这个函数出现异常的时候不会向上抛,而是进程被 std::terminate 杀掉。

(1)Do1() 使用 noexcept 修饰,那么函数中抛异常的时候,不会抛给上一级函数,会直接被 std::terminate 杀掉

(2)Do2() 使用 noexcept(true) 修饰,作用和直接使用 noexcept 修饰相同,函数内的异常不会抛给上一级函数,会直接被 std::terminate 杀掉

(3)Do3() 没有被 noexcept 修饰,函数中抛出的异常,可以抛给上一级函数

(4)Do4() 被 noexcept(false) 修饰,函数中抛出异常,可以抛给上一级函数

#include <iostream>
#include <string>

void Do1() noexcept {
  throw "Do1 exception";
}

void Do2() noexcept(true) {
  throw "Do2 exception";
}

void Do3() {
  throw "Do3 exception";
}

void Do4() noexcept(false) {
  throw "Do4 exception";
}

int main(int argc, char *argv[]) {
  if (argc != 2) {
    std::cout << "argc is not 2\n";
    return 0;
  }

  printf("argv[1] = %s\n", argv[1]);
  try {
    if (argv[1][0] == '1') {
      std::cout << "call Do1()\n";
      Do1();
    } else if (argv[1][0] == '2') {
      std::cout << "call Do2()\n";
      Do2();
    } else if (argv[1][0] == '3') {
      std::cout << "call Do3()\n";
      Do3();
    } else {
      std::cout << "call Do4()\n";
      Do4();
    }
  } catch (const char *e) {
    std::cout << "exception: " << e << std::endl;
  }
  return 0;
}

相关推荐

  1. C++用以声明类一个关键字——struct

    2024-03-27 03:42:03       41 阅读
  2. 音频一些关键词

    2024-03-27 03:42:03       35 阅读
  3. Pythonglobal关键字

    2024-03-27 03:42:03       35 阅读
  4. C#`out`关键字

    2024-03-27 03:42:03       25 阅读
  5. C语言关键字

    2024-03-27 03:42:03       19 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-27 03:42:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-27 03:42:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-27 03:42:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-27 03:42:03       20 阅读

热门阅读

  1. StringRedisTemplate Autowired注入为空解决

    2024-03-27 03:42:03       19 阅读
  2. 20240325 大模型快讯

    2024-03-27 03:42:03       17 阅读
  3. ARM与X86架构的简单剖析与未来展望

    2024-03-27 03:42:03       18 阅读
  4. QT day5

    QT day5

    2024-03-27 03:42:03      16 阅读
  5. C++学习-2

    2024-03-27 03:42:03       17 阅读
  6. AI大模型学习的伦理与社会影响

    2024-03-27 03:42:03       17 阅读
  7. Flutter 获取系统是否是暗黑模式的方式

    2024-03-27 03:42:03       19 阅读
  8. OSI七层模型讲解

    2024-03-27 03:42:03       20 阅读
  9. linux内核网络分析 用户空间工具 “每日读书”

    2024-03-27 03:42:03       21 阅读
  10. C/C++ 设置Socket的IP_TOS选项

    2024-03-27 03:42:03       19 阅读
  11. LeetCode 93. 复原 IP 地址

    2024-03-27 03:42:03       20 阅读
  12. C# 类 结构体

    2024-03-27 03:42:03       18 阅读
  13. SSH公钥(SSH Key)生成方法

    2024-03-27 03:42:03       19 阅读
  14. 判断对象存活的算法

    2024-03-27 03:42:03       20 阅读