C++用户自定义类型

用户自定义类型

可以通过基本类型、const修饰符和声明运算符构造出的类型称为内置类型。内置类型的优点是能够直接有效地展现出传统计算机硬件的特性,但并不能向程序员提供便于书写高级应用程序的上层特性。

结构

构建新类型的第一步通常是把所需要的元素组织成一种数据结构。

new 运算符从一块名为自由存储(free store) (又称为动态内存 (dynamic memory) 或堆 (heap) ) 的区域中分配内存。

把类型的接口(所有代码都可使用的部分)与其现实(对其他不可访问的数据具有访问权限)分离。 C++中实现上述的语言机制被称为类

类含有一系列成员,可能是数据、函数或者类型

  • 类的 public 成员定义该类的接口
  • private 成员则只能通过接口访问。
class Vector {
public:
    // 构建一个 Vector
    Vector(int s): elem{new double[s]}, sz{s} {} // 含义:首先从自由空间获取 s 个 double 类型的元素,并用一个指向这些元素的指针初始化 elem ;然后用 s 初始化 数组。
    double& operator[](int i) { return elem[i]; } // 通过下标访问元素
    itn size() { return sz; }
private:
    double* elem; // 指向元素的指针
    int sz; // 元素的数量
}

枚举

枚举:简单形式用户自定义类型,使得我们可以枚举一系列值。

enum class Color{red, blue, green };
enum class Traffic_light {green, yellow, red};
Color col = Color::red;
Traffic_light light = Traffic_light::red;

枚举类型常用于描述规模较小的整数值合集。通过使用有指代意义的(且易于记忆的)枚举值名字可提高代码的可读性,降低出错的风险。

通过枚举可以在不同的枚举中重复使用枚举值,不至于引起混淆,例如:Color::red 与 traffic_light::red。

enum 后面的 class 指明了枚举是强类型的,且他的枚举值位于指定的作用域中。不同的 enum class 是不同的类型,这有助于防止对常量的意外误用

// 我们不能混用 Traffic_light 和 Color 的值
Color x = red; // 错误:不确定那个 red
Color y = Traffic_light::red; // 错误:这个 red 不是 Color 的对象
Color z = Color::red; // OK
// 我们也不能隐式地混用 Color 和整数值
int i = Color::red; 
Color c = 2;

如果不想显示地限定枚举值名字, 并且希望枚举值可以是 int (无需显示转换),则应该去掉 enum class 中的 class 得到一个“普通的”enum。

默认情况下,enum class 只定义了赋值、初始化和比较(==, <)操作。枚举类型设计一种用户自定义类型,我们可以为它定义别的运算符

Traffic_light& operator++(Traffic_light& t)
    // 前置递增运算符 ++
{
    switch (t) {
        case Traffic_light::green: 
            return t = Traddic_light::yellow;
        case Traffic_light::yellow:
            return t = Traffic_light::red;
        case Traffic_light::red:
            return t = Traffic_light::green;
    }
}

模块化

一个 C++ 程序可能包含许多独立开发的部分,例如函数、用户自定义类型、类层次和模板等。因此构建C+chengxu的关键就是清晰地定义这些组成部分之间的交互关系。第一部也是最重要的一步,是将某个部分的接口和实现分离开来。

==在 C++ 语言中用声明描述接口。

声明指定了使用某个函数或某种类型所需的所有内容==

分离编译

用户代码只能看见所用类型和函数的声明,他们的定义则放置再分离得源文件里,并分别编译

这种机制有助于将一个程序组织成一组版独立的代码片段。
优点:

  • 编译时间减到最少
  • 强制要求程序中逻辑独立的部分分离开来(从而将发生错误的几率降到最低)
  • 一个库通常是一组分离编译的代码片段的集合

严格来说,使用分离编译并不是一个语言问题;而是关门与如何以最佳方式利用特定语言实现的问题。

分离机制在实际的编程过程中非常重要

最好的方法是最大限度地模块化,逻辑上通过语言特性描述模块,而后在物理上通过划分文件及高校分离编译来从分利用模块化。

// Vector.h
#pragma once
#include <iostream>
using namespace std;

class Vector {
public:
	Vector(int s); // 构建一个 Vector
	double& operator[](int i);   // 通过下标访问元素
	int size();
private:
	double* elem; // 指向元素的指针
	int sz;       // 元素的数量
};


// Vector.cpp
#include "Vector.h"
Vector::Vector(int s)					// 构造函数的定义
	:elem{ new double[s] }, sz{ s }		// 初始化成员
{
}

double& Vector::operator[](int i)		// 下标运算符的定义
{
	return elem[i];
}

int Vector::size()						// size()的定义
{
	return sz;
}

名字空间

  • 表达某些声明是属于一个整体的,
  • 表明他们的名字不会与其他名字空间中的名字冲突。

名字空间主要用于组织较大规模的程序组件,最典型的例子是库。使用名字空间,我们就可以很容易地把若干独立开发的部件组织成一个程序。

错误处理

错误处理是一个略显繁杂的主题,他的内容和影响都远远超越了语言特性的层面。***(非常重要)***

在构建应用程序时,通常的做法不是仅仅依靠内置类型(如 char、int、和 double)和语句(如 if、while 和 for),而是建立更多适合应用的新类型(如 string、map 和 regex)和算法(如 sort()、find_if() 和 draw_all() ).**这些高层次的结构简化了程序设计,减少了产生错误的机会,同时也增加了编译器捕获错误的概率。

异常

处理异常由实现者检测可能的异常,然后通知使用者,让使用者可以采取适当的措施。例如vector::operator能够检测潜在的访问错误并且抛出一个 out_of_range 异常

double& Vector::operator[](int i)
{
    if (i < 0 || size() <= i) throw out_of_range{"Vector::operator[]"};
    return elem[i];
}

把可能处理异常的程序放在一个 try 块当中。

void f(Vector& v)
{
    try{
        // 此处的异常将被后面定义的处理模块处理
        v[v.size()] = 7; // 试图访问 v 末尾之后的位置
    }
    catch (out_of_range) {
        // 在此处处理越界错误
    }
}

不变式

假定某事为真的声明称为类的不变式,简称不变式。建立类的不变式是构造函数的任务,他的另一个作用是确保当成员函数退出时不变式仍然成立。

不变式的概念是设计类的关键,而前置条件也在设计函数的过程中起到同样的作用。不变式:

  • 帮助我们准确地理解想要什么;
  • 强制要求具体而明确地描述设计,而这有助于确保代码正确

静态断言

把表达某种期望的语句称为断言(assertion)例如:

static_assert(4 <= sizeof(int), "intefers are roo small"); // 检查整数的尺寸 如果 4 <= sizeof(int) 不满足,将输出 intergers are too small 的信息。

== static_assert 机制能用于任何可以表达为常量表达式的东西==

constexpr double C = 299792.458;

void f(double speed)
{
    const double local_max = 160.0 / (60 * 60);
    
    statuic_assert(speed < C, "can't go that fast"); // 错误:速度必须是个常量
    static_assert(local_max < C, "can't go that fast") // OK
}

==static_assert 最后总要的用途是为泛型编程中作为形参的类型设置断言

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值