【问题标题】:Temporary class object临时类对象
【发布时间】:2020-10-07 17:13:48
【问题描述】:

我目前正在阅读 C++ Primer 5th edition,这是本书中的代码示例之一。我对这行代码auto ret = StrBlobPtr(*this, data->size()); 感到困惑,如果我理解正确,这行代码会创建一个临时 StrBlobPtr 对象并调用此构造函数StrBlobPtr(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {} 但我不明白auto ret= 是如何从临时对象中获取返回值的,所以我的问题是

  1. ret如何获取StrBlobPtr创建的对象?
  2. end 如何返回一个 StrBlobPtr,它保存了 StrBlob 类中 std::shared_ptr<vector<string>> data; 中的最后一个值。

我说的那段代码一直在底部。

#pragma once

#include <vector>
#include <string>
#include <initializer_list>
#include<stdexcept>
#include <memory>
#include <exception>

using std::vector;
using std::string;

class StrBlobPtr;

class StrBlob {
public:
    using size_type = vector<string>::size_type;
    friend class StrBlobPtr;

    StrBlobPtr begin();
    StrBlobPtr end();

    StrBlob() : data(std::make_shared<vector<string>>()) {}
    StrBlob(std::initializer_list<string> il)
        : data(std::make_shared<vector<string>>(il))
    {
    }

    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }

    void push_back(const string& t) { data->push_back(t); }
    void pop_back()
    {
        check(0, "pop_back on empty StrBlob");
        data->pop_back();
    }

    std::string& front()
    {
        check(0, "front on empty StrBlob");
        return data->front();
    }

    std::string& back()
    {
        check(0, "back on empty StrBlob");
        return data->back();
    }

    const std::string& front() const
    {
        check(0, "front on empty StrBlob");
        return data->front();
    }
    const std::string& back() const
    {
        check(0, "back on empty StrBlob");
        return data->back();
    }

private:
    void check(size_type i, const string& msg) const
    {
        if (i >= data->size()) throw std::out_of_range(msg);
    }
private:
    std::shared_ptr<vector<string>> data;
};

class StrBlobPtr {
public:
    StrBlobPtr() : curr(0) {}
    StrBlobPtr(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
    bool operator!=(const StrBlobPtr& p) { return p.curr != curr; }
    string& deref() const
    {
        auto p = check(curr, "dereference past end");
        return (*p)[curr];
    }
    StrBlobPtr& incr()
    {
        check(curr, "increment past end of StrBlobPtr");
        ++curr;
        return *this;
    }

private:
    std::shared_ptr<vector<string>> check(size_t i, const string& msg) const
    {
        auto ret = wptr.lock();
        if (!ret) throw std::runtime_error("unbound StrBlobPtr");
        if (i >= ret->size()) throw std::out_of_range(msg);
        return ret;
    }
    std::weak_ptr<vector<string>> wptr;
    size_t curr;
};
StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); }
StrBlobPtr StrBlob::end()
{
    auto ret = StrBlobPtr(*this, data->size());
    return ret;
}

【问题讨论】:

  • C++ 中的初始化是...complicated。在这种情况下,您会得到Copy Initialization
  • auto x = T();T x(); 相同(除了不解释为函数声明)

标签: c++ c++11 constructor


【解决方案1】:

从c++11开始,如果你不想明确指定变量的类型,你可以使用关键字auto,但不要将其与自动变量混淆,这意味着它们在块的结尾(您可以阅读:cppreference)。

所以在函数的第一行 StrBlobPtr::end() 构造函数被调用,StrBlobPtr 类型的对象被创建,在该复制构造函数被调用之后,另一个对象是已创建,但此对象将一直存在,直到到达块末尾(在本例中为函数末尾)。之后删除第一个对象,因此您只有复制构造函数创建的对象。请查看此帖子:link

如果我是对的,您的第二个问题是关于返回对象。在 c++ 中执行此操作时必须小心。假设您在代码中的某处调用了 StrBlobPtr::end()

StrBlobPtr obj; 
obj = StrBlobPtr::end();

所以你在第一行创建了对象,之后你调用函数,返回相同类型的对象但是这个对象只存在于这一行,在调用 StrBlobPtr::operator=(StrBlobPtr x) 之后从函数返回的对象是已删除且内存中不存在,所以不能这样做:

StrBlobPtr* obj; 
obj = StrBlobPtr::end();

因为在第二行指针obj之后有被删除对象的地址,如果你尝试使用它,你会得到异常。因此,如果要从函数返回整个对象,则必须小心,有时最好使用运算符 new 在动态内存中创建对象并从函数返回引用或指针。如果你经常调用这个函数很重要,每次你需要创建对象并调用复制构造函数来返回这个对象。

【讨论】:

  • 感谢您的回答我想错了,我认为由 StrBlobPtr 创建的临时对象在调用后立即被删除,并且从未复制到ret.
  • @verynice 请注意,编译器很聪明,很可能会消除答案 1 中隐含的所有复制。ret 将直接构造,而不是复制临时对象,临时对象将不存在一点也不。这种行为是 C++17 和更新版本所要求的,但在旧标准下的编译器经常观察到这种行为
猜你喜欢
  • 2019-07-11
  • 2011-03-08
  • 1970-01-01
  • 1970-01-01
  • 2016-01-30
  • 2016-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多