Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/backbone.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么编写func(常量类和值)更可取?_C++_Coding Style - Fatal编程技术网

C++ 为什么编写func(常量类和值)更可取?

C++ 为什么编写func(常量类和值)更可取?,c++,coding-style,C++,Coding Style,为什么要使用func(常量类和值)而不仅仅是func(类值)?当然,现代编译器将使用这两种语法中的任何一种做最有效的事情。这仍然是必要的,还是仅仅是非优化编译器时代的一种拖延 只是补充一下,gcc将为这两种语法生成类似的汇编代码输出。也许其他编译器没有 显然,情况并非如此。很久以前,我从一些代码中得到的印象是gcc做到了这一点,但实验证明这是错误的。这要归功于迈克尔·伯尔,如果在这里给出答案,他对a的回答将被提名 两个签名之间有两个很大的语义差异 第一个是在类型名称中使用&。这表示该值通过引

为什么要使用
func(常量类和值)
而不仅仅是
func(类值)
?当然,现代编译器将使用这两种语法中的任何一种做最有效的事情。这仍然是必要的,还是仅仅是非优化编译器时代的一种拖延

  • 只是补充一下,gcc将为这两种语法生成类似的汇编代码输出。也许其他编译器没有

显然,情况并非如此。很久以前,我从一些代码中得到的印象是gcc做到了这一点,但实验证明这是错误的。这要归功于迈克尔·伯尔,如果在这里给出答案,他对a的回答将被提名

两个签名之间有两个很大的语义差异

第一个是在类型名称中使用
&
。这表示该值通过引用传递。删除此项会导致通过值传递对象,该值实际上会将对象的副本传递到函数中(通过复制构造函数)。对于只需要读取数据的操作(典型的
常量&
),执行对象的完整副本会产生不必要的开销。对于不小或是集合的类,这种开销不是微不足道的


第二个是使用
const
。这可防止函数通过
value
引用意外修改value的内容。它允许调用者在某种程度上保证值不会被函数改变。是的,在许多情况下,传递副本会给打电话的人更深刻的保证

第一个表单不创建对象的副本,它只传递一个指向现有副本的引用(指针)。第二个表单创建一个副本,这可能很昂贵。这并不是优化掉的东西:拥有对象的副本与拥有原始对象之间存在语义差异,复制需要调用类的复制构造函数


对于非常小的类(如如果使用前者,然后尝试更改
),编译器会意外地给您一个错误

如果使用后者,然后尝试更改
,则不会更改


因此前者更容易捕获错误。

< P>第一个例子是通过引用。而不是传递类型,C++将传递对对象的引用(一般来说,引用是用指针实现的…所以它可能是一个大小为4字节的对象)…在第二个示例中,对象是按值传递的…如果它是一个大而复杂的对象,那么它很可能是一个相当重的操作,因为它涉及新“类”的复制构造


现代编译器肯定会做到这一点 使用这两种方法都是最有效的 语法

编译器不会编译你的“意思”,而是编译你告诉它的东西。编译器只适用于低级优化和程序员忽略的问题(如for循环内的计算、死代码等)

在第二个例子中,你告诉编译器要做的是复制一个类——它会不假思索地做——即使你没有使用它,这也是你要求编译器做的


第二个例子明确要求编译器使用相同的变量-节省空间和宝贵的循环(不需要复制)。常量存在错误-因为
Class&value
可以写入(有时需要)在C++中,当编译器为调用方生成代码时,它可能无法访问函数本身的代码。我知道的最常见的调用约定通常有调用方调用复制构造函数,这意味着它是如果没有必要,函数本身的编译不可能阻止复制构造函数。

以下是一些参数声明之间的区别:

copied out modifiable func(Class value) Y N Y func(const Class value) Y N N func(Class &value) N Y Y func(const Class &value) N N N 复制的可修改的 func(类值)Y N Y 函数(常数类值)Y N 函数(类和值)不适用 函数(常量类和值)N 其中:

  • 复制:调用函数时复制输入参数
  • out:value是一个“out”参数,这意味着在func()中所做的修改在函数返回后将在函数外部可见
  • 可修改:值可以在func()中修改
因此
func(类值)
func(常量类值)
之间的区别是:

  • 第一个函数复制输入参数(通过调用类copy构造函数),并允许func()中的代码修改
    value
  • 第二个不复制,不允许func()中的代码修改

    • 有一个巨大的差异,没有人提到过:对象切片。在某些情况下,您可能需要
      常量和
      (或
      &
      )来获得正确的行为

      考虑另一个继承自
      class
      的类
      Derived
      。在客户机代码中,您创建一个
      Derived
      的实例,并将其传递给
      func()
      。如果您有
      func(const class&)
      ,则会传递该实例。正如其他人所说,
      func(class)
      将创建一个副本,您将在
      func
      中有一个新的(临时)
      类的实例(非
      派生的

      如果
      func
      反过来进行向下转换,则行为(而非性能)的这种差异可能很重要。比较运行以下代码的结果:

      #include <typeinfo.h>
      
      struct Class
      {
          virtual void Foo() {};
      };
      class Derived : public Class {};
      
      void f(const Class& value)
      {
          printf("f()\n");
      
          try
          {
              const Derived& d = dynamic_cast<const Derived&>(value);
              printf("dynamic_cast<>\n");
          }
          catch (std::bad_cast)
          {
              fprintf(stderr, "bad_cast\n");
          }
      }
      
      void g(Class value)
      {
          printf("g()\n");
      
          try
          {
              const Derived& d = dynamic_cast<const Derived&>(value);
              printf("dynamic_cast<>\n");
          }
          catch (std::bad_cast)
          {
              fprintf(stderr, "bad_cast\n");
          }
      }
      
      int _tmain(int argc, _TCHAR* argv[])
      {
          Derived d;
          f(d);
          g(d);
          return 0;
      }
      
      #包括
      结构类
      {
      虚拟void Foo(){};
      };
      派生类:公共类{};
      空f(常数等级和值)
      {
      printf(“f()\n”);
      尝试
      {
      const-Derived&d=动态_-cast(值);
      printf(“动态_cast\n”);
      }
      捕获(标准::坏
      
      std::string toUpper( const std::string &value ) {
          std::string retVal(value);
          transform(retVal.begin(), retVal.end(), charToUpper());
          return retVal;
      }
      
      std::string toUpper( std::string value ) {
          transform(value.begin(), value.end(), charToUpper());
          return value;
      }