第3章 通用性【下】

4. 显示转换操作符

概念:
类型转换运算符:operator
类型转换操作符:禁止隐式转换explicit
C++中explicit关键字只能用于修饰只有一个参数的类构造函数 禁止隐式调用类内单参数构造函数。

  1. explicit 禁止隐式调用拷贝构造函数
  2. 禁止类对象之间隐式转换
  3. 只能用来修饰类内部的构造函数
class Test{
   explicit Test(int a); // 禁止隐式调用
}
Test aa(10);//OK 
Test aa = 10;//非法。加入explicit可以有效的防止隐式转换的发生,提高程序质量。
Test bb = aa;// 非法,取消了隐式转换,除非重载操作符“=”

为何要禁止隐式转换示例:

class Test{
public:
    Test(int a);
    bool isSame(Test other){
        return m_val == other.m_val;
    }
private:
    int m_val;    
}
Test a(10);
if(a.isSame(10));// 该语句将返回true 

isSame本来用于比较两个对象,这里对象跟整形居然相等。是因为发生了隐式转换,实际上a是跟一个临时的Test对象进行比较。

示例2

class myDig {
    int value;
public:
    explicit myDig(int n) {
        value = n;
    }
}
void doSomething(myDig num); //函数,接受一个myDig参数,函数体不重要,故省略
void Test1() {
    myDig dig1;           // 错误,没有默认构造函数
    doSomething(dig1);    // 正确
    myDig dig2(10);       // 正确
    doSomething(10);      // 错误,myDig不支持隐式转换
    doSomething(myDig(10)); //正确,调用**类型转换操作符**进行显示转换(也叫转型,cast), 调用myDig构造函数。
}

/*
 * 如果去掉explicit则doSomething(10)正确, 执行了隐式转换
 * 过程相当于执行 myDig tmp(10); doSomething(tmp);
 * 
 * myDig dig3 = 10; //正确,也是隐式转换。
 * /

四种显示转换:
在这里插入图片描述
参考

5. 列表初始化

C++98里允许对数据元素使用{} 进行初始值设定

int arr[5]={0};
int arr[]={1,2,3,4};

C++ 11 中拓展了初始化规则

int a[]={1,3,5};//C++98通过,C++11通过
int b[]{2,4,6};//C++98失败,C++11通过
vector<int> c{1,3,5};//C++98失败,C++11通过
map<int,float>d=
{{1,1.0f},{2,2.0f},{5,3.2f}};//C++98失败,C++11通过

定义在<initializer_list>头文件中
template< class T >
class initializer_list;

使用列表初始化可以防止类型收窄比如:

  1. 浮点数隐式转化为整数
  2. 高精度转化为低精度 long long -> long
  3. 整形转为浮点型,整形大到浮点数无法表示情况
  4. 整形转为较低长度整形,unsigned char = 1024

示例如下

  const int x=1024;
  const int y=10;
  char a=x;//收窄,但可以通过编译
  char*b=new char(1024);//收窄,但可以通过编译
  char c={x};//收窄,无法通过编译
  char d={y};//可以通过编译
  unsigned char e{-1};//收窄,无法通过编译
  float f{7};//可以通过编译
  int g{2.0f};//收窄,无法通过编译
  float*h=new float{1e48};//收窄,无法通过编译
  float i=1.2l;//可以通过编译

6. POD类型

6.1 POD类型的定义和特性

POD : Plain Old Data 简单旧数据 指的是可以通过 简单内存复制 和传输的数据类型。POD对象可以通过memcpy或者其他等价操作进行复制,而且它们的内存布局是完全透明和可预测的。
C++ 中POD类型可以分为两类:trivial 和 standard layout 类型分别可以通过如下判断

template<typename T>struct std::is_trivial;
template <typename T> struct std::is_standard_layout;

Trivial类型
Trivial类型是一种简单的类型,它没有用户定义的构造函数、析构函数或复制操作符,没有私有或保护的非静态成员,没有基类,也没有虚函数。换句话说,trivial类型是一种没有任何特殊语义的类型,它的行为完全由其数据成员决定。例如,一个只包含基本类型(如int、char)成员的struct就是一个trivial类型。
满足如下条件

  1. 它的所有非静态成员都是Trivial类型。
  2. 它是一个类类型(class或struct),但没有用户定义的构造函数。
  3. 它没有虚函数和虚基类。
  4. 它没有非静态成员的类类型或数组,或者所有这些类类型和数组都是Trivial类型。

Standard layout类型

  1. 是一个类类型, 没有虚函数或者虚基类
  2. 所有非静态数据成员都具有相同访问控制
  3. 所有非静态数据成员和基类都是Standard layout类型
  4. 没有多个非静态数据成员的基类
  5. 所有非静态成员,包括在其所有基类中的非静态成员,都有相同的访问控制(public、private、protected)

以下是Standard layout类型示例:

class StandardLayout1 {
public:
    int a;
    char b;
};
struct StandardLayout2 {
public:
    double x;
private:
    StandardLayout1 y;
};

typedef int StandardLayout3[10];

6.2 POD类型重要性

  1. 与C语言兼容
    使用POD类型可以方便的在C与C++之间进行传递数据。
    例如,如果你有一个C语言的库函数,它接受一个指向结构体的指针,你可以在C++中定义一个相同的POD类型,然后传递给这个库函数。

  2. 序列化和网络通信
    POD类型的内存布局是确定的,这使得它们非常适合用于序列化和网络通信。你可以直接将POD类型的对象写入文件或网络套接字,然后在另一端读取并重构对象。
    例如,如果你有一个包含多个数据字段的POD类型,你可以将其序列化为一个字节流,然后通过网络发送到另一台计算机,或者写入文件以便以后读取。

  3. 性能优化
    由于POD类型的对象可以通过简单的内存复制进行复制,因此,使用POD类型可以提高代码的性能。特别是在需要大量复制数据的场景中,使用POD类型可以显著减少CPU的负载。
    例如,如果你有一个大数组,它的元素类型是POD类型,你可以使用std::memcpy函数一次性复制整个数组,这通常比逐个复制数组的元素要快得多。

参考

7. 非受限联合体

联合体(union)是一种构造数据类型,在一个联合体内可以定义多个不同类型的成员,这些成员共享同一块内存空间。C++98不允许有非POD、静态、引用类型成员。C++11之后取消了联合体对数据成员的限制,任何非引用类型都可以成为联合体的数据成员。
示例:

struct Student{
	Student(bool g, int a): gender(g), age(a){}
	bool gender;
	int age;
};
union T{
	Student s;	//C++98下编译失败,不是一个POD类型
	int id;
	char name[10];
};
int main(){
	return 0;
}
//编译选项:g++ -std=c++98 union.cpp

注意事项:
C++11 规定:如果非受限联合体内有非POD的成员,则该成员拥有自定义的构造函数,那么这个非受限联合体的默认构造函数将被编译器删除;其他特殊成员如拷贝构造、拷贝赋值操作符及析构函数也被删除。

#include <string>
using namespace std;
union U {
    string s;
    int n;
};
int main() {
    U u;   // 构造失败,因为 U 的构造函数被删除
    return 0;
}

因为string拥有自己的构造函数,所以U联合体的默认构造函数被删除;修改如下:

#include <string>
using namespace std;
union U {
    string s;
    int n;
public:
    U() { new(&s) string; }
    ~U() { s.~string(); }
};
int main() {
    U u;
    return 0;
}

8. 用户自定义字面量

含义:通过一个后缀标识符,将声明了该后缀标识的字面量转换为需要的相应类型。
示例:

#include<iostream>
int operator "" _h(unsigned long long hour)//注意这里参数必须是unsigned long long类型(可以硬记){
    return hour*60*60;//返回秒
}
int operator "" _m(unsigned long long min){
    return min * 60;
}
int operator "" _s(unsigned long long s){
    return s;
}
int main(){
    std::cout<<1_h<<" "<<1_m<<" "<<1_s;//相当于单位转换的意思
    return 0;
}

注意:

  1. operator"" 与自定义后缀之间必须有空格
  2. 后缀建议以下划线开始,否则会引起编译器警告
  3. C++11 标准中要求声明字面量操作符要遵循如下规则
  • 字面量为整数值接受 unsigned long long 和 const char *
  • 字面量为浮点数接受 unsigned double 或者 const char
  • 字面量为字符串接受 const char* size_t为参数
  • 字面量为字符接受 char为参数

9. 内联命名空间

嵌套命名空间

示例:

namespace S{
    // 第一个嵌套的命名空间,定义了class A
    namespace S1{
        class A {};
    }
    // 第二个嵌套的命名空间,定义了class B
    namespace S2{
        class B {};
    }
}
S::S1::A  // 访问

内联命名空间

含义:内联命名空间中的名字可以被外层命名空间直接使用。

#include <iostream>
namespace Parent{
    namespace Child1{
        void foo() { std::cout << "Child1::foo()" << std::endl; }
    }

    inline namespace Child2{
        void foo() { std::cout << "Child2::foo()" << std::endl; }
    }
}
int main(){
    Parent::Child1::foo();
    Parent::foo();// 无需添加该命名空间的前缀,直接被外层命名空间使用
}

可以解决库函数升级的问题 如:

#include <iostream>
namespace Parent{
    void foo() { std::cout << "foo v1.0" << std::endl; }
}
int main(){
    Parent::foo();
}

升级之后

namespace Parent{
    namespace V1{
        void foo() { std::cout << "foo v1.0" << std::endl; }
    }
    inline namespace V2{
        void foo() { std::cout << "foo v2.0" << std::endl; }
    }
}
int main(){
    Parent::foo();  // 如果向继续使用V1老接口Parent::V1::foo();
}

10. 模版的别名

C++ 11中新增using用法来定义模版别名

template< typename first, typename second, int third>
class SomeType;
 
template< typename second>
typedef SomeType<OtherType, second, 5> TypedefName; // 在C++是不合法的

这不能够通过编译。

为了定义模板的别名,C++11 将会增加以下的语法:

template< typename first, typename second, int third>
class SomeType;
 
template< typename second>
using TypedefName = SomeType<OtherType, second, 5>;

using 也能在 C++11 中定义一般类型的别名,等同 typedef:

typedef void (*PFD)(double);            // 傳統語法
using PFD = void (*)(double);           // 新增語法

11. 一般化的SFINEA规则

在C++模板中,有一条著名的规则,即SFINEA-Substitution failure is not an error,中文直译即是“匹配失败不是错误”。

  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
计算机应用基础第1全文共163页,当前为第1页。 第1 计算机基础知识 计算机应用基础第1全文共163页,当前为第2页。 电子计算机是20世纪人类最伟大的发明之一。计算机技术的飞速发展加快了人类进入信息时代的步伐,计算机的广泛应用改变了人类时代的面貌,特别是微型计算机的出现及计算机网络的发展,使计算机进入了普通家庭,改变了人们的生活方式。计算机逐渐成为人们生活和工作中不可缺少的工具,掌握计算机的应用也成为人们必不可少的技能。 计算机应用基础第1全文共163页,当前为第3页。 世界上第一台电子数字式计算机于1946年2月15日在美国宾夕法尼亚大学正式投入运行,它的名称叫ENIAC(埃尼阿克),是电子数值积分计算机(the electronic numberical intergrator and computer)的英文简称。它使用了17 468个真空电子管,耗电174 kW,占地170 m2,重达30吨,每秒钟可进行5 000次加法运算。虽然它的功能远比不上今天最普通的一台微型计算机,但在当时它已是运算速度的绝对冠军,并且其运算的精确度和准确度也是史无前例的。 ENIAC奠定了电子计算机的发展基础,开辟了一个计算机科学技术的新纪元。有人将其称为人类第三次产业革命开始的标志。 1.1 计算机的发展历史 计算机应用基础第1全文共163页,当前为第4页。 ENIAC诞生后,数学家冯·诺依曼提出了重大的改进理论——存储程序原理,主要内容有3点:其一是电子计算机应该以二进制为运算基础;其二是电子计算机应采用"存储程序"方式工作;其三是进一步明确指出了整个计算机的结构应由5个部分组成:运算器、控制器、存储器、输入装置和输出装置。冯·诺依曼存储程序原理理论的提出,解决了计算机运算自动化和速度配合问题,对后来计算机的发展起到了决定性的作用。直至今天,绝大部分的计算机还是采用冯·诺依曼的方式工作。 早期按冯·诺依曼体系结构设计的计算机有如下一些: 1.1 计算机的发展历史 计算机应用基础第1全文共163页,当前为第5页。 (1)电子离散变量计算机(electronic discrete variable automatic computer,EDVAC)。它是第一个按照存储程序原理设计的计算机,该机1952年投入运行,用于核武器理论计算。 (2)电子延迟存储自动计算机(electronic delay storage automatic calculator,EDSAC)。它是第一次实现大型存储程序的计算机,1949年投入运行。 (3)通用自动计算机(universal automatic computer,UNIVAC)。1951年作为商品计算机投入使用,开创了用于数据处理的计算机新时代。 1.1 计算机的发展历史 计算机应用基础第1全文共163页,当前为第6页。 计算机的发展到目前为止共经历了如下4个时代。 (1)从1946年到1959年,这段时期称为"电子管计算机时代"。第一代计算机的内部元件使用的是电子管。由于一部计算机需要几千个电子管,每个电子管都会散发大量的热量,因此,如何散热是一个令人头痛的问题。电子管的寿命最长只有3 000小时,计算机运行时常常发生由于电子管被烧坏而使计算机死机的现象。第一代计算机主要用于科学研究和工程计算。 (2)从1960年到1964年,由于在计算机中采用了比电子管更先进的晶体管,所以将这段时期称为"晶体管计算机时代"。晶体管比 1.1 计算机的发展历史 计算机应用基础第1全文共163页,当前为第7页。 电子管小得多,不需要暖机时间,消耗能量较少,处理更迅速、更可靠。第二代计算机的程序语言从机器语言发展到汇编语言。接着,高级语言FORTRAN和COBOL相继被开发出来并被广泛使用。这时,开始使用磁盘和磁带作为辅助存储器。第二代计算机的体积和价格都下降了,使用的人也多起来了,计算机工业迅速发展。第二代计算机主要用于商业、大学教学和政府机关。 (3)从1965年到1970年,集成电路被应用到计算机中,因此这段时期被称为"中小规模集成电路计算机时代"。集成电路(integrated circuit,IC)是集成在晶片上的一个完整的电子电路, 1.1 计算机的发展历史 计算机应用基础第1全文共163页,当前为第8页。 这个晶片比手指甲还小,却包含了几千个晶体管元件。第三代计算机的特点是体积更小、价格更低、可靠性更高、计算速度更快。第三代计算机的代表是IBM公司花了50亿美元开发的IBM 360系列。这一阶段最主要的是在第三代计算机中出现了操作系统,代表着计算机系统的形成和完善。 (4)从1971年到现在,被称为"大规模集成电路计算机时代"。第四代计算机使用的元件依然是集成电路,不过,这种集成电路

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值