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

本文介绍了C++中的几个关键特性:explicit用于明确构造函数,delete用于禁用拷贝构造和赋值,default自动生成默认构造函数,override用于标记函数重载,final阻止继承和函数覆盖,noexcept控制异常传播。文章通过实例展示了这些概念在实际编程中的应用。
摘要由CSDN通过智能技术生成

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;
}
  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值