什么是良好的编程风格,良好编程风格六大原则

  什么是良好的编程风格,良好编程风格六大原则

  Yyds干货库存

  类是c语言中代码的基本单位,显然,它们被广泛使用。本节列出了编写类时的注意要点。

  施工人员职责的一般描述。不要在构造函数中调用虚函数,不要在不能报错的情况下初始化可能失败的虚函数。

  可以在定义构造函数中执行各种初始化操作。

  优点:不需要考虑类是否初始化。构造函数完全初始化后,对象可以是const类型,也可以更方便地被标准容器或算法使用。缺点:如果在构造函数中调用自己的虚函数,这种调用不会重定向到子类的虚函数实现。即使目前没有子类化的实现,但它仍然是未来的隐患。构造函数很难在不使程序崩溃(因为它并不总是合适的方法)或使用异常(因为它已被禁用)的情况下报告错误。如果执行失败,会得到一个初始化失败的对象,可能会进入异常状态。您必须使用bool IsValid()或类似的机制来检查它。然而,这是一个非常容易被忽视的方法。无法获取构造函数的地址,所以比如构造函数做的工作无法用简单的方式交给其他线程。结论构造函数不允许调用虚函数。如果代码允许,直接终止程序是处理错误的适当方式。否则,考虑使用Init()方法或工厂函数。

  构造函数不得调用虚函数或试图报告非致命错误。如果需要对一个对象进行非平凡的初始化,可以考虑使用显式Init()方法或工厂模式。避免在没有其他状态的对象上使用init()方法,这些状态会影响可以调用的公共方法(这种半构造的对象有时不能正常工作)。

  隐式类型转换的一般描述不定义隐式类型转换。对于转换运算符和单参数构造函数,请使用explicit关键字

  定义隐式类型转换允许在需要另一种类型(称为*目标类型*)的地方使用一种类型(称为*源类型*)的对象,例如,将int类型参数传递给需要double类型的函数。

  除了语言定义的隐式类型转换,用户还可以通过在类定义中添加适当的成员来定义自己需要的转换。在源类型中定义隐式类型转换可以通过目的类型名的类型转换运算符(如运算符bool())来实现。在目的类型中定义隐式类型转换,可以由构造函数以源类型为唯一参数(或者唯一没有默认值的参数)来实现。

  Explicit关键字可以用在构造函数或(C 11中引入的)类型转换运算符中,以确保类型转换只能在调用点显式声明目标类型时执行,例如,使用cast。这不仅作用于隐式类型转换,还作用于C 11的列表初始化语法:

  Foo类

  显式Foo(int x,double y);

  .

  };

  void Func(Foo f);此时不允许使用以下代码:

  Func({42,3.14 });//Error这段代码技术上不是隐式类型转换,但是语言标准认为是应该限制的显式行为。

  优点有时候目的类型名称一目了然。通过避免显式写出类型名,隐式类型转换可以使类型更易于使用和表达。隐式类型转换可以简单地代替函数重载。初始化对象时,列表初始化语法是一种简洁明了的写法。缺点隐式类型转换可以隐藏类型不匹配的错误。有时,目标类型并不符合用户的期望,用户甚至没有意识到类型转换已经发生。隐式类型转换会使代码难以阅读,尤其是在有函数重载的情况下,因为很难判断调用的是哪个函数。单参数构造函数可能被无意中用作隐式类型转换。如果单参数构造函数没有添加explicit关键字,读者就无法判断这个函数是否要作为隐式类型转换使用。或者作者忘记添加显式标签。没有明确的方法来判断哪个类应该提供类型转换,这样会使代码不明确。如果目标类型是隐式指定的,那么列表初始化将会遇到与隐式类型转换相同的问题,尤其是当列表中只有一个元素时。结论是在类型定义中,类型转换运算符和单参数构造函数应该用explicit标记。一个例外是,复制和移动构造函数不应标记为explicit,因为它们不执行类型转换。对于设计为透明包装其他类型的类,隐式类型转换有时是必要且合适的。这时候你要联系项目负责人,说明特殊情况。

  不能用一个参数调用的构造函数不应添加显式。接受std:initializer_list作为参数的构造函数也应该省略explicit以支持复制初始化(例如,MyType m={1,2 };).

  复制类型和可移动类型概述如果你的类型需要,让它们支持复制/移动。否则,禁用隐式复制和移动功能。

  定义一个可复制类型允许一个对象在初始化时从另一个相同类型的对象获取一个值,或者在赋值时从另一个相同类型的对象获取一个值,而不改变源对象的值。对于用户定义的类型,复制操作通常由复制构造函数和复制赋值运算符定义。字符串类型是可复制类型的一个例子。

  可移动类型允许对象在初始化时从相同类型的临时对象中获取值,或者在赋值时被赋予相同类型的临时对象的值(因此,所有可复制的对象也是可移动的)。std:unique_ptr int是一个可以移动但不能复制的对象的例子。对于用户定义的类型,move操作一般是通过移动构造函数和移动赋值运算符来实现的。

  在某些情况下,编译器会隐式调用复制/移动构造函数。例如,对象通过值传递。

  优点可移动、可复制的对象可以通过传值的方式传递或返回,这使得API更简单、更安全、更通用。与传递指针和引用不同,这种传递不会造成所有权、生命周期、可变性等方面的混淆,所以不需要在协议中明确。同时,它还防止了客户端和非作用域中的实现之间的交互,使它们更容易理解和维护。这种对象可以与需要传递值的通用API一起使用。

  一般来说,复制/移动构造函数和赋值操作比它们的替代方法(如Clone()、CopyFrom()或Swap())更容易定义,因为它们可以由编译器隐式或通过=default生成。这种方式简洁。它还确保所有数据成员都将被复制。复制和移动构造函数通常更有效,因为它们不需要堆分配或单独的初始化和赋值步骤。同时,它们更适合于优化,例如省略不必要的副本。

  Move操作允许源数据隐式地、高效地转出正确的值对象,这有时会使代码风格更加清晰。

  缺点很多类型是不需要复制的,给他们提供复制操作会很混乱很荒谬不合理。单件类型(Registerer),与特定范围相关的类型(Cleanup),逻辑上,与其他对象实体紧密耦合的类型(Mutex)不应该提供复制操作。为基类提供复制/赋值操作是有害的,因为当使用它们时,对象会被剪切。复制操作的默认或任意实现可能是不正确的,这通常会导致混乱和难以诊断的错误。

  复制构造函数是隐式调用的,也就是说,这些调用很容易被忽略。这将会令人困惑,特别是对于那些使用语言约定或强制引用的程序员。同时会在一定程度上鼓励过度复制,导致性能问题。

  结论如果有必要的话,让你的字体可以复制/删除。根据经验,如果这种复制操作对用户来说不是一目了然的,就不要让类型可复制。如果你使一个类型可复制,你必须同时给出复制构造函数和赋值操作的定义,反之亦然。如果你把类型做成可移动的,移动操作的效率比复制操作高,那么定义移动的两个操作(移动构造函数和赋值操作)。如果类型不能复制,但是move操作的正确性对用户来说是显而易见的,那么就把类型设置为只可移动,并定义move的两个操作。

  如果定义了复制/移动操作,请确保这些操作的默认实现是正确的。记住总是检查默认操作的正确性,并在文档中声明该类可以被复制和/或移动。

  Foo类

  公共:

  Foo(Foo other):field _(other . field){ }

  //差,只定义了move构造函数,没有定义对应的赋值运算符。

  私人:

  field field _;

  };因为有对象切割的风险,所以不要为任何可能有派生类的对象提供赋值操作或者复制/移动构造函数(当然也不要继承有这种成员函数的类)。如果您的基类需要可复制的属性,请为要实现的派生类提供一个公共虚拟克隆()和一个受保护的复制构造函数。

  如果您的类不需要复制/移动操作,请在公共域中使用=delete或其他方式显式禁用它。

  //MyClass既不可复制也不可移动。

  my class(const my class)=delete;

  MyClass运算符=(const my class)=delete;结构与类的一般描述仅在只有数据成员时使用struct,其他情况下使用class。

  说明struct和class关键字在c中的含义几乎相同,我们在这两个关键字中加入自己的语义理解,从而为定义的数据类型选择合适的关键字。

  Struct用于定义包含数据的被动对象,也可以包含相关的常量,但除了访问数据成员之外没有其他功能。此外,访问函数是通过直接访问位域,而不是函数调用。除了构造函数、析构函数、Initialize()、Reset()、Validate()等类似的用于设置数据成员的函数外,不能提供其他函数。

  如果需要更多的函数,class更合适。如果您不确定,请使用class。

  为了和STL保持一致,我们可以用struct代替class来模仿函数。

  注意:类和结构的成员变量使用不同的命名规则。

  使用组合继承概述(YuleFox注:这也是GoF在设计模式中反复强调的)往往比使用继承更合理。如果使用继承,则定义为公共继承。

  当子类被定义继承基类时,子类包含父基类的所有数据和操作的定义。c在实践中,继承主要用于两种情况:实现继承,子类继承父类的实现代码;接口继承,子类只继承父类的方法名。

  优点:通过完整地重用基类代码,实现继承减少了代码量。因为继承是在编译时声明的,所以程序员和编译器都能理解相应的操作并发现错误。从编程的角度来看,接口继承用于强制一个类输出特定的API。当一个类未能实现API中的一个必要方法时,编译器也会发现并报告错误。

  缺点对于实现继承来说,由于子类的实现代码分散在父类和子类之间,理解子类的实现更加困难。子类不能重写父类的非虚函数,当然也不能修改其实现。基类也可能定义一些数据成员,所以基类的实际布局也必须加以区分。

  结论所有继承必须是公共的。如果要使用私有继承,应该用将基类的实例作为成员对象来替换它。

  不要过度使用实现继承。组合往往更合适。尽量只在继承是“一”的情况下使用(“是-a”,Yule Fox注:其他“有-a”的情况请使用组合):如果Bar确实是“一种”Foo,那么Bar可以继承Foo。

  如有必要,析构函数被声明为虚拟的。如果你的类有虚函数,析构函数也应该是虚函数。

  对于可能被子类访问的成员函数,不要过度使用protected关键字。请注意,数据成员必须是私有的。

  对于重载的虚函数或虚析构函数,使用override或(不常用的)final关键字显式标记它们。早期(C 11之前)的代码可能会使用虚拟关键字作为最后的手段。因此,在声明重载时,请使用override或final virtual。如果标记为override或final的析构函数不是基类虚函数的重载,编译将报告一个错误,这有助于捕捉常见错误。这些标记作为文档,因为如果这些关键字被省略,代码阅读器必须检查所有的父类,以确定该函数是否是虚函数。

  ,

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • office2010激活密钥大全 怎么永久激活office2010
  • project2010产品密钥免费_project2010激活密钥永久激活码
  • c语言调用退出函数 c语言退出整个程序怎么写
  • c语言中怎么给函数初始化 c语言的初始化语句
  • c语言编写函数计算平均值 c语言求平均函数
  • chatgpt是什么?为什么这么火?
  • ChatGPT为什么注册不了?OpenAI ChatGPT的账号哪里可以注册?
  • OpenAI ChatGPT怎么注册账号?ChatGPT账号注册教程
  • chatgpt什么意思,什么是ChatGPT ?
  • CAD中怎么复制图形标注尺寸不变,CAD中怎么复制图形线性不变
  • cad中怎么创建并使用脚本文件,cad怎么运行脚本
  • cad中快速计算器的功能,cad怎么快速计算
  • cad中快速修改单位的方法有哪些,cad中快速修改单位的方法是
  • cad中心点画椭圆怎么做,cad轴测图怎么画椭圆
  • CAD中常用的快捷键,cad各种快捷键的用法
  • 留言与评论(共有 条评论)
       
    验证码: