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;
}