C ++ - const members / return const int& vs return int

时间:2011-03-31 07:23:27

标签: c++

那些C ++行是什么意思?是否有其他方式来编写它们?

const int& a() const;
int getA() const;

感谢。

5 个答案:

答案 0 :(得分:10)

这两个是类中成员函数的两个可能签名,它承诺不更改对象本身。在第一种情况下,它将返回对整数(可能是成员属性)的常量引用,引用为const意味着调用者将无法使用它来更改内部属性。第二种情况是它按值返回一个整数。

语义上略有不同,但在大多数情况下它们并不重要,将它们视为获取值的两个函数。对于会产生影响的情况,请参阅:

class test {
public:
   test() : m_value() {
      std::cout << &m_value << std::endl; // print where the attribute is
   }
   int const & getValue() const {
      return m_value;
   }
   int copyValue() const {
      return m_value;
   }
   void setValue( int value ) {
      m_value = value;
   }
private:
   int m_value;
};
int main() {
   test t;                      // will printout an address [1]
   int v1 = t.getValue();       // caller copies the value
   int v2 = t.copyValue();      // caller copies the value (itself a copy in hte calle)
   int const &r = t.getValue(); // reference to t.m_value
   int const &c = t.copyValue();// reference to *copy* [2]
   std::cout << v1 << v2 << r << c
      << std::cout;             // 0000
   std::cout << &v1 << &v2      // 4 pointers, the third is [1] a r *is* t.m_value
      << &r << &c << std::cout; //     the rest should be different
   t.setValue( 5 );
   std::cout << v1 << v2 << r   // 0050, v1 and v2 where copies, r *is* t.m_value
      << c << std::cout;
}

标有[2]的行使用该语言的奇怪的功能,如果您获得常量引用到r值(临时),编译器将绑定临时引用并保持活动直到引用超出范围(基本上它将r值临时转换为隐藏变量并将引用绑定到它)。

我添加该行是明确的,因为行为的差异不是由于接收端main制作维护引用的副本,而是(准确) )通过访问者的签名。

答案 1 :(得分:6)

两者都是实现相同目标的等效方式:

const int& a() const;
int getA() const;

您正在返回一个值。方法头右侧的 const 是一个注释函数getA()和一个()不会修改将执行它们的对象(隐藏的this参数)。这在编译时很重要,因为这意味着在编译时会考虑额外的验证。在运行时,上面的函数与这些函数没有区别:

const int& a();
int getA();

然而,扩展编译器的验证功能的巨大好处(当你不指望它发生时什么也没有改变)显然值得额外 const

要担心的第二部分是两个函数的返回类型,它们代表它们之间的主要区别,并且可能是问题的动机。让我们将主题改为不同的功能:

std::string getName() const;
   { return name; }

这里,返回值可能是类中的name属性。返回是通过值进行的,这意味着从此方法返回时将创建属性的副本。当字符串很大并且您在应用程序周围按值移动大量字符串时,这可能是一个问题。然后它恰好出现了返回引用机制,它承诺不复制:

std::string &getName() const
   { return name; }

这实际上非常有趣:我们正在返回一个引用,而不是该对象的副本。引用类似于指针,因此您只需复制指针(32位系统中的4个字节)而不是整个对象。 这很有希望。但是,它甚至不会编译。编译将抱怨您在承诺方法为 const 时返回引用,因此,不会修改它将执行的对象。此代码将允许非法操作发生:

Person p( "Baltasar" );
p.getName() = "José";

cout << p.getName() << endl;

这就是为什么返回类型的 const 显示为一个新的有吸引力的选项,可以解决问题。常量引用不允许修改它指向的对象,使用:

const std::string &getName() const
    { return name; }

它现在将编译,而之前的恶意代码不会。 现在让我们回到我们的问题:

const int &getA() const;
int a() const;

第二个是按值返回,这意味着将在返回时复制 int (4个字节)。第一个意味着将返回对 int (4个字节)的常量引用。可以看出,在这种情况下使用按引用返回而不是按值返回没有性能优势。

根据经验,通过const引用返回始终是安全的,它永远不会比按值返回更昂贵。

答案 2 :(得分:2)

const int& a() const;

a()返回对int的const引用。最后的const修饰符表示它无法更改调用它的对象的状态。

int getA() const;

与上面的描述相同,只是返回类型为int,如果收集了返回变量的副本。

什么意思说什么时候不能改变对象的状态?

class foo
{
    int m_Var ;
    public:
        foo(int arg1) : m_Var(arg1){}
        void mutableMethod()
        {
            m_Var = 20 ;  // "this" has the variable m_Var and
                          //  by assigning it a value changes the state of
                          //  m_Var. Changing the state of it's member variable
                          //  is meant changing the state of object.
        }

        void nonMutableMethod() const
        {
             m_Var = 20 ;  //  This assignment is not allowed because of const 
                           //  modifier. The method is not allowed to change the
                           //  the state of object on which it is called ( this )
        }
};

此外,常量方法不能通过非const引用返回成员变量。

答案 3 :(得分:2)

关键的区别在于:

  • getA()返回一个int数据值,调用者可以使用该值完全独立于程序的任何其他部分
  • a()会返回对int选择的某些a()的引用:
    • int x = a()“示例”当时的int值,逻辑上等同于int x = getA()
    • const int& x = a() 保存对()返回的变量的引用!

保存引用并不总是符合您的期望或想要

  • GOOD:如果该变量是临时/文字(例如const int& x = a()const int& a() { return 3; }

  • ,编译器就足够聪明地制作该变量的副本
  • GOOD或 BAD?(取决于它在应用中是否有意义):每次稍后读取x的值时,程序可能会(尝试)从int内部返回的原始a()变量重新读取它:如果该变量的值已经更改,那么x的值也将更改。 (“可能”,因为无论如何,优化者都可以避免这种情况)

  • UGLY :如果该地址的内存不再存储该变量(例如,它位于new内存,那是delete d) ,然后尝试读取x的值可能会导致不可预测的值使应用程序崩溃(如果内存地址不再可读)。

a()getA()都是类的成员函数;我们知道这一点,因为只有成员函数可以是const,这在技术上表明他们不能在不丢弃其常量的情况下更改非mutable数据成员,但这种限制背后的意图是它们不应该修改对象的调用者可观察值;可变数据通常用于缓存,调试跟踪等。

答案 4 :(得分:0)

我们可以将返回的引用转换为指针,因此从理论上讲,它比返回副本(值)提供更多信息(地址和值)。

并且可以将const ref修改为const_cast可变。

无论如何,编译器都会尝试使用原始寄存器,地址或立即数中的值​​。

哪个目标的替代方式?确保始终保持正确状态不会增加额外的工作。对于const引用和const,我发现方便使用CR和CN宏。

(253,253,253)(254,254,254)(254,254,254)(253,253,253)(254,254,254)(254,254,254)(254,254,254)(254,254,254)(254,254,254)(254,254,254)
(254,254,254)(254,254,254)(255,255,255)(254,254,254)(254,254,254)(254,254,254)(254,254,254)(254,254,254)(255,255,255)(255,255,255)
(254,254,254)(255,255,255)(255,255,255)(254,254,254)(255,255,255)(255,255,255)(255,255,255)(255,255,255)(255,255,255)(255,255,255)
(253,253,253)(254,254,254)(254,254,254)(254,254,254)(254,254,254)(254,254,254)(254,254,254)(254,254,254)(254,254,254)(254,254,254)
(254,254,254)(254,254,254)(255,255,255)(254,254,254)(255,255,255)(255,255,255)(254,254,254)(255,255,255)(255,255,255)(255,255,255)
(254,254,254)(254,254,254)(255,255,255)(254,254,254)(255,255,255)(255,255,255)(254,254,254)(255,255,255)(254,254,254)(254,254,254)
(254,254,254)(254,254,254)(255,255,255)(254,254,254)(254,254,254)(254,254,254)(254,254,254)(254,254,254)(255,255,255)(255,255,255)
(254,254,254)(254,254,254)(255,255,255)(254,254,254)(255,255,255)(255,255,255)(254,254,254)(255,255,255)(254,254,254)(254,254,254)
(254,254,254)(254,254,254)(255,255,255)(254,254,254)(255,255,255)(255,255,255)(254,254,254)(255,255,255)(255,255,255)(255,255,255)
(254,254,254)(254,254,254)(255,255,255)(254,254,254)(255,255,255)(255,255,255)(254,254,254)(255,255,255)(255,255,255)(255,255,255)

副作用是声明变得越来越短,并且随着行数越来越短,行数也减少了。味道的问题。但是当与DMAP宏结合使用时,它似乎具有优势。

DownloadTask task = new DownloadTask();
        String result = null;
        try {

            result = task.execute("http://www.posh24.se/kandisar").get();
            Log.i("Content Downloaded",result);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        Log.i("Result",result);

    }

    //type of vatiable which we will send to class
    public class DownloadTask extends AsyncTask<String, Void, String> {
        @Override
        protected String doInBackground(String... urls) {
            String result="";
            URL url;
            HttpsURLConnection urlConnection = null;
            try
            {
                url=new URL(urls[0]);
                urlConnection=(HttpsURLConnection) url.openConnection();
                InputStream in = urlConnection.getInputStream();
                InputStreamReader reader  = new InputStreamReader(in);
                int data = reader.read();
                while(data!=-1)
                {
                    char current=(char) data;
                    result+=current;
                    data=reader.read();
                }
                return result;
            }
            catch(Exception e)
            {
                e.printStackTrace();
                return "Failed";
            }
        }

vs

#define CN  const
#define CR  const&         // Constant reference
#define CDa const*         // mutable pointer to constant data
#define CPD const * const  // constant pointer to constant data
const int&  verbose() const;
int CR      shorter() CN;

如果不使用自动且使用迭代器,则该示例将显示超过333%的差异。