为什么我们要送const的“类型”的参考,而不是仅仅是类型名称来构造(why do we have

2019-10-22 03:30发布

我尝试做一个简单的程序(是的,它是一门功课),可以生成日期,及最喜欢正常的人:我做了我的类属性私有,我试图发送一个即时通讯工作的构造类型相同,但该编译器还没有接受它,我做了一些研究和我发现,在这样的人慷慨地送一个const“类型”参考构造女巫案件的意思,我认为有不明白的OOP很好
所以为什么我们要送const的“类型”的参考,而不是仅仅的类型来命名的构造? &请给我初学者一些链接或网站
这里是我的代码的和平:

 class Date {  
     int d ;   
     int m ;   
     int y ;   
 public :   
      Date();   
      Date(int , int , int);   
      Date(const Date &);// my question is : why do we have to write this instead of Date( Date )   
 };

PS:对不起,我的英语

Answer 1:

套用我们的问题:

为什么我们写Date(const Date &) ,而不是Date(Date)

我要这个分成两部分,第一部分回答为什么一个拷贝构造函数需要采取每参考的参数,第二个为什么这必须是一个常量引用。


一个拷贝构造函数需要采取每参考其观点的理由是,对于要带每份参数的函数void f(T arg)当你把它叫做f(obj) obj复制arg 使用T的副本构造函数 。 所以,如果你想实现的拷贝构造函数,你最好不要采取通过复制的说法,因为这会调用拷贝构造函数,而调用它,从而导致无限递归。 您可以轻松地此尝试自己:

struct tester {
  tester(tester) {std::cout << "inside of erroneous copy ctor\n";}
};

int main()
{
  tester t1;
  std::cout << "about to call erroneous copy ctor\n";
  tester t2(t1);
  std::cout << "done with call erroneous copy ctor\n";
  return 0;
}

该方案不仅应该永远写一行,然后吹堆栈。
注:由于丹尼斯在他的评论中指出,其实这节目不能保证编译,所以,这取决于你的编译器,你可能不是真的能尝试一下。

底线: 一个拷贝构造函数应该通过引用其说法,因为服用每份需要拷贝构造函数。


这使得它为什么是个问题const T&而不是简单的T& 事实上,有两个原因。
逻辑的原因是,当你调用拷贝构造函数,你不要指望从复制的对象改变。 在C ++中,如果你想表达的东西是不可改变的,你使用const 。 这告诉他们可以安全地通过他们的贵重物品到您的拷贝构造函数的用户,因为除了从它读也不会对它做任何事。 作为一个好的副作用,如果实现了拷贝构造函数和无意尝试写入对象时,编译器在你抛出一个错误信息,提醒您给调用者的承诺的。
另一个原因是,你不能绑定临时对象到非const引用,你只能将它们绑定到const引用。 临时对象,例如,什么功能可能会返回:

struct tester {
  tester(tester& rhs) {std::cout << "inside of erroneous copy ctor\n";}
};

tester void f()
{
   tester t;
   return t;
}

f()被调用时,一个tester对象内部创建的,然后它的一个副本被返回给调用者,那么这可能把它变成另一个副本:

tester my_t = f(); // won't compile

的问题是, f()返回一个临时对象,并且为了调用拷贝构造函数,该临时将需要绑定到rhs的参数tester的拷贝构造,这是一个非const参考。 但是你可以临时对象不绑定到一个非const引用,因此代码将不会编译。
虽然你可以解决这一点,如果你想(只是不复制暂时的,但它绑定到const引用,而不是,它扩展了临时的一生的时间去参考的生命周期结束: const tester& my_t = f()人希望能够复制你的类型的临时对象。

底线: 复制构造应采取其参数的const引用 ,否则用户可能不愿意或不能使用它。


另外还有一个事实:在未来的C ++标准,您可以重载函数的临时对象,所谓的rvalues 。 所以,你可以有一个特殊的拷贝构造函数重载的“正常”的拷贝构造函数的临时对象。 如果您有已经支持这一新功能编译器,你可以试一下:

struct tester {
  tester(const tester&  rhs) { std::cout << "common copy ctor\n"; }
  tester(      tester&& rhs) { std::cout << "copy ctor for rvalues\n"; }
};

当您使用上面的代码来调用我们的f()

tester my_t = f();

当通过调用返回的临时对象右值的新拷贝构造函数应该叫f()复制到my_t和定期拷贝构造函数可能会以副本叫t对象从内部f()返回的临时。 (注意:您可能必须禁用编译器的优化,以看到这一点,因为编译器被允许优化掉所有的拷贝。)
那么,你可以用这个? 那么,当你复制一个右值,你就知道该对象从会调用拷贝构造函数后要销毁拷贝,因此拷贝构造函数取右值( T&& )可能只是从参数值而不是复制他们的。 由于对象将被销毁无论如何,没有人会注意到。
对于某些类(例如,用于串类),从一个对象移动的值来另一个可能比复制它们便宜得多。



Answer 2:

如果我理解正确你的问题,以避免拷贝/调用对象的构造函数。

void function(const T&); // does not create new T
void function(T&); // does not create newT, however T must be modifiable (lvalue)
void function(T); // creates new T 

对于简单类型创建新的副本是微不足道的(通常由编译器优化掉)。 对于复杂的对象,创建新的副本可能是非常昂贵的。 因此,你按引用传递它。

https://isocpp.org/wiki/faq/references

https://isocpp.org/wiki/faq/ctors

如果你问为什么不能做到以下几点:

struct type {
  type(type);
};

就是因为这将导致无穷递归,因为构造函数依赖于自身

但是你可以这样做

struct type {
  type(type, int);
};

因为此构造是从合成的不同type(const type&)

http://en.wikipedia.org/wiki/Copy_constructor



Answer 3:

除了@ AAA的回答,我会尽量回答const的一部分。 该const部分只是意味着要传递逻辑目标不会改变。 这是有道理的,因为当一个拷贝构造函数被调用Date对象参数dd不应该在所有的修改!

您可以删除const和你的代码仍然工作方式相同。 然而, const提供了额外的安全性,你永远无法修改标记为变量的const 。 在你的情况,这意味着你不能调用任何非const方法的Date 。 这是通过在编译时,编译器执行。



Answer 4:

历史上,这是引入引用的语言的原因。 这里有一个解释:

在C可以通过值(值传递给参数void f(struct custom_type i)或通过指针( void g(struct custom_type* i)

随着POD值( intchar等)路过值是没有问题的,但如果你正在寻找复杂的结构,那么栈通过将整个结构上堆栈函数调用的过快增长。 这就是为什么在c您倾向于通过结构由指针参数,即使该函数不修改它们。

在C ++中有这样的情况,其中这两个选项的工作:

  • 路过指针涉及为运营商违反直觉的语法(如果定义operator +为一class custom_type写入custom_type a, b, c; a = &b + &c;是违反直觉的作为a没有得到分配的地址的总和。此外,如果你想成为能够分配的值的总和a 地址的总和a ,你将不得不情况之间莫名其妙区分,通过语法)。

  • 路过值在复制构造的情况下是不可能的或不期望的。 在你的情况,如果你有Date(Date d) {}和分配Date a; Date b(a); Date a; Date b(a); 你得到了什么是副本a创建为参数的构造函数只是传递b 。 这导致无限递归,如创建的副本a传递作为参数涉及相同Date d = a; b = Date(d); Date d = a; b = Date(d);

出于这些原因(有可能已经被其他人)的决定是为了创建references :看起来语法的值等类型的数据类型,但表现得像指针(也就是说,它指向另一个变量的值,但你访问它像变量,而不是像一个指针)。

至于为什么你在声明中需要常量的原因,是为了让你的构造将接受临时对象。 正如你不能修改一个临时参考的价值,如果你的构造不接受const&您只能使用非const对象稳定的拷贝构造函数。

也就是说,如果您有:

class Date
{
public:
    Date(Date& other); // non-const reference
    ...

你可以写:

Date a;
Date b = a;

但不是:

Date someFunction() { return Date(xxx); }
Date a = someFunction(); // someFunction returns a temporary object

既不是:

const Date someImportantDate;
Date a = someImportantDate; // cannot pass const value to non-const 


文章来源: why do we have to send the const “ type ” reference instead of just the types name to the constructor
标签: c++ oop