一、default和delete关键字

(一)编译器提供的“缺省函数”

  1.类的成员函数:构造/析构函数、复制构造/复制赋值函数、移动构造/移动赋值函数

  2. 类的全局默认操作函数:operator new/delete、operator,、operator*、operator->、operator->*等。

(二)“=default”

  1. default:显式指示编译器生成该函数的默认版本,但仅用于类的特殊成员函数(含析构函数)。

  2. 当类中自定义了构造函数后,该类将不再是POD类型(可用is_pod检查),但使用default可“恢复”其POD特质。

  3. default既可以在类体里定义,也可以在类外定义

  (三) “=delete”

  1. 禁止使用某个函数。必须在函数第一次声明的时候将其声明为delete。

  2. 不同于default,任何函数(含非成员函数或模板函数)都可以delete。

  3. 在函数重载或模板特化中,可用delete来滤掉一些函数的形参类型,以禁止编译器做一些不必要的类型转换或阻止特定的模板实例化

  4. explicit和delete混用会带来混乱。因此在使用delete显式删除时,应该总是避免用explicit来修饰函数,反之亦然。

【编程实验】default和delete关键字

#include <iostream>
using namespace std; class Widget
{
private:
int data; public:
Widget() = default; //指示编译器提供默认版本(不影响POD特质)
Widget(int i):data(i){}
Widget(double d) = delete; //删除double版本
explicit Widget(char c) = delete; //注意,这里explicit与deltete混用,将产生一些混乱。 Widget(const Widget&) = delete;
Widget& operator=(const Widget&);//这里没使用default,本例将在类外定义 private:
//2.3.2 特化版本
//Widget 类中声明了一个模板函数,当进行模板特化时,要求禁止参数为 void* 的函数调用。
//本意是按照 C++98 的“私有不实现”思路,将特例化的函数声明为private。但模板特化不能放在
//类作用域中定义,它必须放在命名空间作用域中定义。见后面类外定义部分
//template<>
//void processPointer<void>(void*); //编译失败
public:
//2.3 delete在模板特化中的作用
//2.3.1 泛化版本
template<typename T>
void proccessPointer(T* ptr){}
}; //在类外使用“=default”来指明使用默认版本
inline Widget& Widget::operator=(const Widget&) = default; //2. delete重载函数
template<>
void Widget::proccessPointer<void>(void*) = delete; // 仍然是public, 但被delete void Func(Widget w){} void overloadFunc(int i) {};
void overloadFunc(char c) = delete; //显式删除char版本 //4. delete妙用
//4.1 禁止在堆上创建类对象!
class NoHeapAlloc
{
public:
void* operator new(std::size_t) = delete; //注意这里!
}; //4.2 禁止在栈上创建类对象!
class NoStackAlloc
{
public:
NoStackAlloc()
{
cout <<"NoStackAlloc()" << endl;
}
~NoStackAlloc() = delete; //注意这里,将导致无法自动析构函数。
}; int main()
{
cout <<is_pod<Widget>::value << endl; //0 //1. 测试default与delete
Widget w; //ok,调用无参构造函数,己被声明为default;
Widget w1;
//Widget w2(w1); //无法编译通过,因为Widget(const Widget&)己被delete
//Widget w3 = w1; //无法编译通过,因为Widget(const Widget&)己被delete
Widget w4;
w4 = w1; //ok,operator=被指显式定为的默认函数 //2. delete重载函数
//2.1 delete成员函数
Widget w5();
//Widget w6(1.0); //error, double版本的构造函数被delete Func();
//Func(1.0); //error, 1.0转为Widget需要调用double版本的构造函数,但该函数己delete。 //2.2 delete普通函数
overloadFunc();
//overloadFunc('a'); //编译失败,char版本被delete。同时'a'也就没就机会隐式转为int。 //2.3 delete特化的模板函数
int* pi = nullptr;
void* pv = nullptr;
w5.proccessPointer(pi); //ok;
//w5.proccessPointer(pv); //error,void*版本的特化函数被delete //3. explicit与delete的冲突
//Widget w6('a'); //error,char版本的构造函数被delete
Func('a'); //编译通过!!!由于char版本的构造函数被声明为explicit,'a'将无法再隐式转为Widget,但
//可以隐式转为int,于是调用Widget(int)版本。这与Widget(char)声明为delete的初衷不符!!! //4. delete的妙用!
//4.1 仅限在栈上创建对象
NoHeapAlloc nh;
//NoHeapAlloc* pnh = new NoHeapAlloc; //编译失败,因为operator new己被delete //4.2 仅限在堆上创建对象
void* p = malloc(sizeof(NoStackAlloc));
//NoStackAlloc nsa; //编译失败!栈对象,会被自动析构,但析构函数己被delete。
new (p) NoStackAlloc(); //placement new构造的对象(仍会调用构造函数来初始化对象),但编译器不会为
//其调用析构函数(可用于单例模式)。
return ;
}
/*输出结果
0
NoStackAlloc()
*/

二、override和final关键字

(一)final两个作用

  1. 作用于类时,可以禁止该类被用作基类。

  2. 作用于虚函数时,会阻止它在派生类中被重写(override)。

(二)override:强制重写虚函数

  1. 重写必须满足的条件

  (1)基类中的函数必须是虚函数。

  (2)基类和派生类中的函数名字必须完全相同(析构函数除外)、函数形参的类型必须完全相同。

  (3)基类和派生类的函数的常量性必须完全相同

  (4)基类和派生类中的函数返回值和异常规格必须兼容。

  (5)基类和派生类中的函数引用饰词必须完全相同(详见《知识扩展》部分)

  2. 知识扩展——左值/右值引用类型的重载函数(&和&&)

  (1)可以利用引用饰词(&或&&)进行函数的重载。

  (2)左值引用类型的重载函数:形如,retType& func()&。(注意,返回左值,该函数仅在*this是左值时调用)

  (3)右值引用类型的重载函数:形如,retType func()&&。(注意,返回右值,该函数仅在*this是右值时调用)。

  (4)成员函数末尾加引用饰词(&和&&),类似末尾加const情形,后者表明只有*this为const才能调用。

【编程实验】override和final关键字

#include <iostream>
#include <vector>
#include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id_with_cvr; //辅助类模板,用于打印T的类型
template <typename T>
void printType(string s)
{
cout << s << " = " << type_id_with_cvr<T>().pretty_name() << endl;
} //1. 重写(override)的条件
class Base
{
public:
virtual void f1() const {};
virtual void f2(int x) {}
virtual void f3() & {};
void f4() const {};
}; //class Derived : public Base
//{
//public:
// virtual void f1() override {}; //error,const常量性不同!基类为void f1() const
// virtual void f2(unsigned int x) override{} //error,函数形参类型不同(基类为void f2(int x))
// virtual void f3()&& override{} //error,引用饰词不同(基类为void f3() & )
// virtual f4() const override{} //error,非虚函数,不能override
//}; //2. final用于类和虚函数
class MathObject //数学类(接口类)
{
public:
virtual double Arith() = ; //算术运算
virtual void Print() = ; //打印
}; class Printable : public MathObject
{
public:
void Print() final //为了保证打印风格统一,加final以阻止在子类中被重写
{
cout << "Output is : " << Arith() << endl;
}
}; class Add final: public Printable //在类上加final表示该类不再被继承
{
double x, y;
public:
Add(double a, double b):x(a),y(b){}
double Arith() { return x + y; }
}; //class Add3 : public Add //Add被声明为final,无法作为基类
//{
//}; //3. 左值 /右值引用类型的重载函数
class Widget
{
public:
using DataType = std::vector<double>; //左值引用类型版本
DataType& data() & //对于*this为左值时,调用该函数。注意返回引用
{
cout <<"invoke DataType& data() & " << endl;
return values;
} //右值引用类型版本
DataType data() && //对于*this为右值时,调用该函数。注意返回右值
{
cout << "invoke DataType data() && " << endl;
return std::move(values);
}
private:
DataType values;
}; //工厂函数
Widget makeWidget()
{
return Widget();
}
int main()
{
//左值/右值引用类型的重载函数
Widget w; decltype(auto) vals1 = w.data(); //由于w是左值,会调用DataType& data() &
decltype(auto) vals2 = makeWidget().data();//由于makeWidget()返回临时对象(是个右值),会调用DataType data() && printType<decltype(vals1)>("vals1");
printType<decltype(vals2)>("vals2"); return ;
}
/*输出结果
invoke DataType& data() &
invoke DataType data() &&
vals1 = class std::vector<double,class std::allocator<double> > &
vals2 = class std::vector<double,class std::allocator<double> >
*/

第10课 面向对象的增强(default/delete、override/final)的更多相关文章

  1. Java程序员应该了解的10个面向对象设计原则

    面向对象设计原则: 是OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心,但大多数Java程序员追逐像Singleton.Decorat ...

  2. (转)Java程序员应该了解的10个面向对象设计原则

    面向对象设计原则是OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心,但大多数Java程序员追逐像Singleton.Decorator ...

  3. [译]Java 程序员应该了解的 10 个面向对象设计原则

    面向对象设计原则是OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心,但大多数Java程序员追逐像Singleton.Decorator ...

  4. Java 程序员应在2019年学习的10条面向对象(OOP)设计原则

    面向对象的设计原则 是 OOP 编程的核心,但是我看到大多数 Java 程序员都在追求诸如 Singleton 模式,Decorator 模式或 O​​bserver 模式之类的设计模式,而对学习面向 ...

  5. C++2.0新特性(三)——<=default,=delete、alias(别名)、noexcept、override、final、以及和const对比>

    一.=default,=delete 1.首先我们要回顾一下类默认函数的概念: C++中,当我们设计与编写一个类时,若不显著申明,则类会默认为我们提供如下几个函数: (1)构造函数(A()).(2)析 ...

  6. C++中 =default,=delete用法

    =default: 用于显式要求编译器提供合成版本的四大函数(构造.拷贝.析构.赋值) 例如: class A{ public: A() = default; A(const A& a) = ...

  7. Java程序员应当知道的10个面向对象设计原则

    面向对象设计原则是OOPS编程的核心, 但我见过的大多数Java程序员热心于像Singleton (单例) . Decorator(装饰器).Observer(观察者) 等设计模式,而没有把足够多的注 ...

  8. python第三十八课——面向对象(一)

    1.面向对象:(思想) 面向:看.关注.瞅 对象:个体.实体.实例.结果单词:object在python中一些皆对象 面向过程:(思想) 面向:看.关注.瞅 过程:经过.经历.从头到尾 使用一些生活中 ...

  9. 第10课 C++中的新成员

    1. 动态内存分配 (1)C++通过new关键字进行动态内存申请,是以类型为单位来申请空间大小的 (2)delete关键字用于内存释放 ▲注意释放数组时要加[],否则只释放这个数组中的第1个元素. [ ...

随机推荐

  1. Qt 的两个许可证区别分析:LGPL 和商业协议

    Qt 的两个许可证区别分析:LGPL 和商业协议 Qt 有两个许可证:LGPL 和商业协议.这两个协议在现在的 Qt 版本中的代码是完全一致的(潜在含义是,Qt 的早期版本,商业版的 Qt 通常包含有 ...

  2. linux 下 的串口模拟器 minicom 退出方法

    ctrl + a (或 A) 进入 minicom 的配置模式:终端外观上无任何变化! 然后按下 z (或 Z) 方可打开配置帮助界面 然后按下 x (或 X)退出

  3. How to get the free disk space in PostgreSQL (PostgreSQL获取磁盘空间)

    Get the current free disk space in PostgreSQL PostgreSQL获取磁盘空间 from eshizhan Here has a simple way t ...

  4. 一种优化操作list、数组的多线程解决方案。

    这几天接触到了一些操作list的功能,由于list太长,加上每条数据的处理时间,导致性能下降,正好利用学来的多线程知识和网上的资料结合实践一番,写出了一个通用类如下. /** * 操作数组的线程 * ...

  5. 浅析负载均衡的6种算法,Ngnix的5种算法

    常见的几种负载均衡算法 1.轮询法 将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载. 2.随机法 通过系统的随机算法,根据后端服务器的 ...

  6. 编译OpenCV提示opencv_contrib缺少boostdesc_bgm.i等文件

    错误提示: ~/opencv_contrib/modules/xfeatures2d/src/boostdesc.:: fatal error: boostdesc_bgm.i: No such fi ...

  7. delphi实现窗体闪烁功能

    delphi实现窗体闪烁功能 以前做窗口闪动时都没有考虑到让任务栏上的按钮闪动的问题, 现在一个客户需要任务栏按钮闪动,发现以前使用的flashwindow不能达到要求了, 查找了一下,找到flash ...

  8. 用chrome的snippets片段功能创建页面js外挂程序,从控制台创建js小脚本

    用chrome的snippets片段功能创建页面js外挂程序,从控制台创建js小脚本 Chrome的snippets是小脚本,还可以创作并在Chrome DevTools的来源面板中执行.可以访问和从 ...

  9. CTF-代码审计(1)——parse_str()变量覆盖

    题目连接:http://222.18.158.226:7000/iscc.php 考点:parse_str()变量覆盖 代码: PHP知识点: 1.parse_url() 参照网址:https://w ...

  10. LabWindows/CVI第一章:基本规则

    一. #include<stdio.h>      //头文件,#号是预处理指令,standard input output header的缩写. void main()          ...