【问题标题】:How to return a class object by reference in C++?如何在 C++ 中通过引用返回类对象?
【发布时间】:2012-02-13 10:20:04
【问题描述】:

我有一个名为 Object 的类,它存储一些数据。

我想使用这样的函数通过引用返回它:

    Object& return_Object();

然后,在我的代码中,我会这样称呼它:

    Object myObject = return_Object();

我已经编写了这样的代码并且它可以编译。但是,当我运行代码时,我总是遇到段错误。通过引用返回类对象的正确方法是什么?

【问题讨论】:

标签: c++ reference segmentation-fault return-by-reference


【解决方案1】:

您可能正在返回堆栈上的对象。也就是说,return_Object() 大概是这样的:

Object& return_Object()
{
    Object object_to_return;
    // ... do stuff ...

    return object_to_return;
}

如果这是你正在做的事情,那么你就不走运了——object_to_return 已经超出范围并在return_Object 的末尾被破坏,所以myObject 指的是一个不存在的对象。您要么需要按值返回,要么返回一个在更大范围内声明的Object,或者将newed 返回到堆中。

【讨论】:

    【解决方案2】:

    你只能使用

         Object& return_Object();
    

    如果返回的对象具有比函数更大的范围。例如,如果您有一个封装了它的类,则可以使用它。如果在函数中创建对象,请使用指针。如果要修改现有对象,请将其作为参数传递。

      class  MyClass{
          private:
            Object myObj;
    
          public:
             Object& return_Object() {
                return myObj;
             }
    
             Object* return_created_Object() {
                return new Object();
             }
    
             bool modify_Object( Object& obj) {
                //  obj = myObj; return true; both possible
                return obj.modifySomething() == true;
             }
       };
    

    【讨论】:

    • 但是,如果在我的调用代码中我说:MyClass mc; Object outsideObj = mc.return_Object; 如果我随后修改 outsideObj 的属性,那实际上会修改封装在 mc 中的 myObj 吗?跨度>
    • 对象外对象 = mc.return_Object();将引发实例outsideObj 的复制构造。它现在是一个单独的实例,修改一个是否会影响另一个取决于复制构造函数的实现方式。
    【解决方案3】:

    您只能通过引用返回非本地对象。析构函数可能使某些内部指针或其他任何内容无效。

    不要害怕返回值——it's fast

    【讨论】:

    • 复制省略对我来说是非常宝贵的一课。谢谢!
    【解决方案4】:

    我会给你一些例子:

    第一个例子,不返回本地范围对象,例如:

    const string &dontDoThis(const string &s)
    {
        string local = s;
        return local;
    }
    

    你不能通过引用返回local,因为localdontDoThis的主体末尾被销毁。

    第二个例子,可以通过引用返回:

    const string &shorterString(const string &s1, const string &s2)
    {
        return (s1.size() < s2.size()) ? s1 : s2;
    }
    

    在这里,您可以通过引用返回 s1s2,因为它们是在调用 shorterString 之前定义的。

    第三个例子:

    char &get_val(string &str, string::size_type ix)
    {
        return str[ix];
    }
    

    使用代码如下:

    string s("123456");
    cout << s << endl;
    char &ch = get_val(s, 0); 
    ch = 'A';
    cout << s << endl; // A23456
    

    get_val 可以通过引用返回s 的元素,因为调用后s 仍然存在。

    第四个例子

    class Student
    {
    public:
        string m_name;
        int age;    
    
        string &getName();
    };
    
    string &Student::getName()
    {
        // you can return by reference
        return m_name;
    }
    
    string& Test(Student &student)
    {
        // we can return `m_name` by reference here because `student` still exists after the call
        return stu.m_name;
    }
    

    用法示例:

    Student student;
    student.m_name = 'jack';
    string name = student.getName();
    // or
    string name2 = Test(student);
    

    第五个例子:

    class String
    {
    private:
        char *str_;
    
    public:
        String &operator=(const String &str);
    };
    
    String &String::operator=(const String &str)
    {
        if (this == &str)
        {
            return *this;
        }
        delete [] str_;
        int length = strlen(str.str_);
        str_ = new char[length + 1];
        strcpy(str_, str.str_);
        return *this;
    }
    

    然后您可以像这样使用上面的operator=

    String a;
    String b;
    String c = b = a;
    

    【讨论】:

      【解决方案5】:

      好吧,它在代码中可能不是一个非常漂亮的解决方案,但它在你的函数界面中确实很漂亮。而且效率也很高。如果第二个对您来说更重要(例如,您正在开发一个库),这是理想的选择。

      诀窍是这样的:

      1. A a = b.make(); 行在内部被转换为 A 的构造函数,即就像您编写了 A a(b.make());
      2. 现在b.make() 应该会生成一个带有回调函数的新类。
      3. 这整个事情只能通过类来处理,不需要任何模板。

      这是我的最小示例。只检查main(),你可以看到它很简单。内部不是。

      从速度的角度来看:Factory::Mediator 类的大小只有 2 个指针,多于 1,但不多。而这是整个事物中唯一被价值转移的对象。

      #include <stdio.h>
      
      class Factory {
        public:
          class Mediator;
      
          class Result {
            public:
              Result() {
                printf ("Factory::Result::Result()\n");
              };
      
              Result(Mediator fm) {
                printf ("Factory::Result::Result(Mediator)\n");
                fm.call(this);
              };
          };
      
          typedef void (*MakeMethod)(Factory* factory, Result* result);
      
          class Mediator {
            private:
              Factory* factory;
              MakeMethod makeMethod;
      
            public:
              Mediator(Factory* factory, MakeMethod makeMethod) {
                printf ("Factory::Mediator::Mediator(Factory*, MakeMethod)\n");
                this->factory = factory;
                this->makeMethod = makeMethod;
              };
      
              void call(Result* result) {
                printf ("Factory::Mediator::call(Result*)\n");
                (*makeMethod)(factory, result);
              };
          };
      };
      
      class A;
      
      class B : private Factory {
        private:
          int v;
      
        public:
          B(int v) {
            printf ("B::B()\n");
            this->v = v;
          };
      
          int getV() const {
            printf ("B::getV()\n");
            return v;
          };
      
          static void makeCb(Factory* f, Factory::Result* a);
      
          Factory::Mediator make() {
            printf ("Factory::Mediator B::make()\n");
            return Factory::Mediator(static_cast<Factory*>(this), &B::makeCb);
          };
      };
      
      class A : private Factory::Result {
        friend class B;
      
        private:
          int v;
      
        public:
          A() {
            printf ("A::A()\n");
            v = 0;
          };
      
          A(Factory::Mediator fm) : Factory::Result(fm) {
            printf ("A::A(Factory::Mediator)\n");
          };
      
          int getV() const {
            printf ("A::getV()\n");
            return v;
          };
      
          void setV(int v) {
            printf ("A::setV(%i)\n", v);
            this->v = v;
          };
      };
      
      void B::makeCb(Factory* f, Factory::Result* r) {
            printf ("B::makeCb(Factory*, Factory::Result*)\n");
            B* b = static_cast<B*>(f);
            A* a = static_cast<A*>(r);
            a->setV(b->getV()+1);
          };
      
      int main(int argc, char **argv) {
        B b(42);
        A a = b.make();
        printf ("a.v = %i\n", a.getV());
        return 0;
      }
      

      【讨论】:

        【解决方案6】:

        返回一个已启动的对象并不是一个很好的做法,因为它确实超出了范围。在极少数情况下,这是所需的选项。如果类是引用计数智能指针或其他一些智能指针,它实际上可以完成。 How does a reference-counting smart pointer's reference counting work?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-10-01
          • 2014-12-11
          • 1970-01-01
          • 2021-08-08
          • 2020-06-18
          • 1970-01-01
          • 2021-11-08
          相关资源
          最近更新 更多