C++设计与声明超详细讲解

目录
  • 让接口被正确使用不易被误用
  • 宁以pass-by-reference-to-const替换pass-by-value
  • 必须返回对象时
  • 将成员变量声明为private
  • 以non-member non-friend替换member函数
  • non-member
  • 考虑写出一个不抛出异常的swap函数

让接口被正确使用不易被误用

除非有好的理由,否则应该让你的types的行为与内置types一致,请拿ints做范本

提供行为一致的接口!

阻止误用的办法包括建立新类型,限制类型上的操作,束缚对象值(比如要统计年月日,限制月的大小在1-12),消除客户的资源管理责任(智能指针)

shared_ptr支持定制型删除器,可被用来自动解除互斥锁等

宁以pass-by-reference-to-const替换pass-by-value

值传递要调用copy构造函数,释放时要调用多次析构函数(有副本),费时

const的必要性:引用传递代替值传递时,确保不会对传入的数据做改变

防止被切割:

class Window{
public:
    virtual void display();
}
class WindowWithScrollBars:public Window{
public:
    virtual void display();
}
void show(Window w){
    w.display();
}

当用一个WindowWithScrollBars对象传入show时,因为是值传递,会导致其特化信息被切割,变成了一个Window对象!无法多态了!

应该如下:传进来的窗口是什么类型,w就表现出哪种特性

void show(const Window& w){
    ...
}

说到底,引用是指针实现出来的,引用传递说到底也是传递的指针,如果有一个对象属于内置类型,值传递效率会比引用传递效率高一些。

值传递对于内置类型和STL的迭代器和函数对象来说代价不贵,其他类型还是选用const引用传递好!

必须返回对象时

别妄想返回reference

不是所有情况都是引用传递好

const A& operator*(const A& rhs){
    A result(rhs);//调用构造函数
    return result;
}

返回了一个result的引用,但result是一个局部变量,离开作用域就被析构了!!!

引用不能返回一个局部对象,否则一败涂地

一个必须返回新对象的函数的做法是:就让他返回一个新对象呗

const A operator*(const A& rhs){
    A result(rhs);//调用构造函数
    return A(rhs);
}

承受一个operator*构造和析构函数的代价即可

绝对不要返回一个指针或引用指向一个local stack对象(出作用域会被析构),或返回引用指向一个heap-allocated对象(无法保证合理的delete),或返回指针或引用指向一个local static对象而有可能同时需要多个这样的对象(一个指针修改了指向对象的参数后,其他指针指向的参数也被修改了)

将成员变量声明为private

语法一致性:成员变量不是public,用户只能通过public里的相应函数来访问成员变量,用户使用时就都有一致的使用规则(全都要使用小括号等)

使用函数可以对成员变量的处理有更精确的控制,如可以编写const函数实现只读访问,不加const实现读写访问等

封装性,防止成员变量被更改

假如有一个public成员变量,我们最终取消了它,所有使用它的代码都会被破坏,假如有一个protected成员变量,我们最终取消了它,所有使用它的派生类都会被破坏。因此protected其实并不比public更加具有封装性

说到底,选择private就好

以non-member non-friend替换member函数

能够访问private成员变量的函数只有class的member函数加上friend函数,如果要在一个member函数(不只可以访问private数据,也能取用private函数、enums、typedefs等)和一个non-member,non-friend函数做抉择,较好封装性的时后者。因为它并不增加能够访问class内private成分的函数数量

将所有便利函数放在多个头文件内但同属于一个命名空间,用户可以轻松添加这一组便利函数,即可以添加更多的non-member,non-friend函数到此命名空间

参考C++标准程序库,vector、algorithm等,导入头文件再进行调用,即可完成很多事情

non-member

若所有参数皆需要类型转换,请为此采用non-member函数

class Rational{
public:
    Rational(int numerator=0,int denominator=1);
    int numerator() const;
    int denominator() const;
    const Rational operator* (const Rational& rhs) const;
}
Rational onehalf(1,2);
Rational result=onehalf*2;//很好!
Rational result=2*onehalf;//不行!

原因在于:

result=onehalf.operator*(2);//发生了隐式转换 得益于之前没有将构造函数声明为explicit
result=2.operator*(onehalf);

2没有相应的class,没有operator*成员函数,当然无法执行

结论为:只有当参数被列于参数列内,这个参数才是隐式转换的合格参与者

改变做法为将operator*变成non-member函数,允许编译器在每个实参上执行隐式转换

operator*是否应该成为class的一个friend函数呢?否定的,因为operator*完全可以借用Rational的public接口完成任务,这告诉我们:member函数的反面是non-member,而不是friend

如果你需要为某个函数的所有参数(包括this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是non-member

考虑写出一个不抛出异常的swap函数

当做swap时,如果交换内部的指针,效率就高了呀

以指针指向一个对象,内含真正的数据,即pimpl手法(pointer to implementation)

class WidgetImpl{
public:
    ...
private:
    int a,b,c;
}
class Widget{
public:
    void swap(Wideget& other){
        using std::swap;//必要的,在找不到class里的swap函数调用此函数
        swap(p,other.p);
    }
private:
    WidgetImpl* p;
}
//修订后的std::swap特化版本
namespace std{
    template<>
    void swap<Widget>(Widget& a,Widget& b)
    {
        a.swap(b);//调用a的swap成员函数
    }
}

这种方法和STL有一致性,因为STL也提供有public的成员函数和std::swap的特化版本

如果swap的默认版本的效率你可以接受,那不需要做任何事

如果swap的默认版本实现效率不足:

1、提供一个public swap成员函数,让它高效的置换两个对象(pimpl)

2、在class的命名空间内提供一个non-member swap,并用它调用上诉swap成员函数。

3、如果正在编写一个class,为class特化std::swap,并用它调用你的swap成员函数。

到此这篇关于C++设计与声明超详细讲解的文章就介绍到这了,更多相关C++设计与声明内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C和C++ const的声明差异

    当在C源代码文件中将变量声明为const时,可以通过以下方式实现: const int i = 2; 然后,可以在另一个模块中使用此变量,如下表示: extern const int i; 但若要获取与 C++ 中相同的行为,则必须将 const 变量声明为: extern const int i = 2; 如果希望在 C++ 源代码文件声明用于 C 源代码文件的 extern 变量,请使用: extern "C" const int x = 10; 以防止 C++ 编译器进行名称重整

  • C++设计与实现ORM系统实例详解

    目录 介绍 依赖关系 设计思路 项目进度 数据库通用接口 实例构造 智能查询方式设计 单元测试 运行方法 介绍 我们通用的ORM,基本模式都是想要脱离数据库的,几乎都在编程语言层面建立模型,由程序去与数据库打交道.虽然脱离了数据库的具体操作,但我们要建立各种模型文档,用代码去写表之间的关系等等操作,让初学者一时如坠云雾.我的想法是,将关系数据库拥有的完善设计工具之优势,来实现数据设计以提供结构信息,让json对象自动映射成为标准的SQL查询语句.只要我们理解了标准的SQL语言,我们就能够完成数据

  • C++ API功能设计的实现

    目录 前言 纯C API 面向对象的C++ API 基于模板的API 数据驱动型API 前言 创建类来表示API中的每个关键对象,同时提供这些类的方法 此处的API风格指的是如何表现API的功能,以下4种: 纯C API 可以用C编译器编译的API.这种API只包含一组自由函数以及辅助的数据结构和常量.这种风格的接口不包含对象或继承,因此被称为纯C模式 面向对象的C++ API 这种风格涉及对象(其中包含相关的数据与方法)的使用以及继承.封装和多态等概念的应用 基于模板的API 通过模板功能,C

  • C++超详细讲解贪心策略的设计及解决会场安排问题

    目录 问题描述 贪心策略 算法设计 代码实现 选择结构体 随机输入会议 按结束时间排序 最终会议确定 结束语 问题描述 设有n个会议的集合C={1,2,…,n},其中每个会议都要求使用同一个资源(如会议室),而在同一时间内只能有一个会议使用该资源.每个会议i都有要求使用该资源的起始时间bi和结束时间ei,且bi < ei .如果选择了会议i使用会议室,则它在半开区间[bi, ei)内占用该资源.如果[bi, ei)与[bj , ej)不相交,则称会议i与会议j是相容的.会场安排问题要求在所给的会

  • C语言函数声明以及函数原型超详细讲解示例

    C语言代码由上到下依次执行,原则上函数定义要出现在函数调用之前,否则就会报错.但在实际开发中,经常会在函数定义之前使用它们,这个时候就需要提前声明. 所谓声明(Declaration),就是告诉编译器我要使用这个函数,你现在没有找到它的定义不要紧,请不要报错,稍后我会把定义补上. 函数声明的格式非常简单,相当于去掉函数定义中的函数体,并在最后加上分号;,如下所示: dataType functionName( dataType1 param1, dataType2 param2 ... ); 也

  • Java超详细讲解SpringMVC如何获取请求数据

    目录 1.获得请求参数 1)基本类型参数:​​​​​​​   2)POJO类型参数: 3)数组类型参数   4)集合类型参数   2.请求乱码问题 3.参数绑注解@RequestParam​​​​​​​ 4.获得Restful风格的参数 5.自定义类型转换器 6.获得请求头 7.文件上传 8.小结 1.获得请求参数 客户端请求参数的格式是:name=value&name=value- - 服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数: 1)基本类型

  • C语言 struct结构体超详细讲解

    目录 一.本章重点 二.创建结构体 三.typedef与结构体的渊源 四.匿名结构体 五.结构体大小 六.结构体指针 七.其他 一.本章重点 创建结构体 typedef与结构体的渊源 匿名结构体 结构体大小 结构体指针 其他 二.创建结构体 先来个简单的结构体创建 这就是一个比较标准的结构体 struct people { int age; int id; char address[10]; char sex[5]; };//不要少了分号. 需要注意的是不要少了分号. 那么这样创建结构体呢? s

  • C语言函数超详细讲解下篇

    目录 前言 函数的声明和定义 函数声明 函数定义 举例 简单的求和函数 把加法单独改写成函数 添加函数声明 带头文件和函数声明 静态库(.lib)的生成 静态库文件的使用方法 函数递归 什么是递归? 递归的两个必要条件 练习1 一般方法 递归的方法 练习2 一般方法 递归方法 练习3 一般方法 递归方法 练习4 一般方法 递归方法 递归与迭代 递归隐藏的问题 如何改进 选递归还是迭代 总结 前言 紧接上文,继续学习函数相关内容. 函数的声明和定义 函数声明 告诉编译器有一个函数叫什么,参数是什么

  • C语言数组超详细讲解中篇三子棋

    目录 前言 1.三子棋是什么? 1.1 百度百科 1.2 游戏编程准备工作 2. 程序实现 2.1 搭建程序框架 2.2 模块化编程 2.2.1 源文件test.c 2.2.2 源文件play.c 2.2.3 头文件play.h 2.3 程序实现—拓展play函数 2.3.1 棋盘初始化与打印函数 2.3.2 玩家下棋函数 PlayMover 2.3.3 电脑下棋函数 ComputerMove 2.2.4 判断赢家函数 WhoIsWin 总结 前言 本文主要是对前面所学内容进行复习和练习,学习内

  • C++超详细讲解隐藏私有属性和方法的两种实现方式

    目录 例子 用抽象类解决问题 用Pimpl风格解决问题 总结 参考 在我们编写程序的时候,会将程序模块化,常见的就是用动态链接库的方式,然后导出函数接口或者类.而对于导出类的方式,作为模块的实现者,不论是给第三方使用或者自己的项目使用,应该都不太愿意暴露自己的私有属性和方法,个人碰到的主要有以下两个常见原因: 通过隐藏私有属性和方法,让被调用者猜不到其实现方式 私有方法中或者属性中,可能会存在一些第三方的头文件或者库的依赖,而对于被调用方来说不应该直接依赖 本文将介绍两种方式来满足以上的需求,一

  • C++函数模板与重载解析超详细讲解

    目录 1.快速上手 2.重载的模板 3.模板的局限性 4.显式具体化函数 5.实例化和具体化 6.重载解析 6.1 概览 6.2 完全匹配中的三六九等 6.3 总结 7.模板的发展 1.快速上手 函数模板是通用的函数描述,也就是说,它们使用泛型来定义函数. #include<iostream> using namespace std; template <typename T> void Swap(T &a,T &b);//模板原型 struct apple{ st

  • java反射超详细讲解

    目录 Java反射超详解✌ 1.反射基础 1.1Class类 1.2类加载 2.反射的使用 2.1Class对象的获取 2.2Constructor类及其用法 2.4Method类及其用法 Java反射超详解✌ 1.反射基础 Java反射机制是在程序的运行过程中,对于任何一个类,都能够知道它的所有属性和方法:对于任意一个对象,都能够知道它的任意属性和方法,这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制. Java反射机制主要提供以下这几个功能: 在运行时判断任意一个对象所属

  • 超详细讲解Linux DHCP服务

    目录 一.DHCP服务(动态主机配置协议) 1.背景 2.概述 3.优点 4.DHCP报文类型 5.DHCP 的分配方式 二.安装 DHCP 服务器 1.DHCP 服务软件 2.主配置文件 三.配置步骤 1.使用 DHCP 动态的给 PC 机分配 IP 地址 ① eNSP ②虚拟机 ③验证 ④进入命令行"ipconfig"测试 一.DHCP服务(动态主机配置协议) 1.背景 1.手动设置工作量大且容易冲突 2.用DHCP可以减少工作量和避免地址冲突 2.概述 作用:为局域网内的电脑分配

随机推荐