【问题标题】:How to stop the compiler from deleting my function如何阻止编译器删除我的函数
【发布时间】:2017-09-08 09:01:35
【问题描述】:

当我在重载的operator= 末尾添加return 语句时,编译器会抛出错误。它说:

'File::File(const File&)' 被隐式删除,因为默认定义格式不正确

但是,当我删除 return 语句(和类型说明符)时,没问题。

class File : public Document {
private:
    fstream mainFile;
    string drive, folder, fileName, fullPath;
protected:

public:
    File(string d, string f, string fn, string txt = "NULL") : Document(txt) {
        drive = d;
        folder = f;
        fileName = fn;
        if(fileName.find(".txt") == -1) {
            fileName.append(".txt");
        }
        fullPath = drive + ":/" + folder + "/" + fileName;
        mainFile.open(fullPath.c_str());
        mainFile << txt;
    }
    File() : Document() {
        drive = folder = fileName = "NULL";
        fullPath = drive + ":/" + folder + "/" + fileName;
    }

    File operator = (File & a) {
        this->getDrive() = a.getDrive();
        this->getFolder() = a.getFolder();
        this->getFileName() = a.getFileName();
        this->getText() = a.getText();
        this->fileName = a.fileName;
        return a;
    }

};

【问题讨论】:

  • 顺便说一句,this-&gt; 符号不是必需的。仅当局部变量或参数与成员具有相同名称时才使用它。您可以更改参数和局部变量以避免this-&gt;this-&gt; 也不需要执行类方法。
  • 在查看find的结果时优先使用std::string::npos而不是-1。
  • 您正在尝试返回传入引用的副本。该类不知道如何构造副本。你需要写一个拷贝构造函数
  • 默认定义格式不正确,因为默认定义会按成员进行复制,而无法复制 fstream 对象。因此,编译器不知道如何处理mainFile 成员。

标签: c++ file


【解决方案1】:
File operator = (File & a) {

此运算符按值返回其结果。也就是说,通过副本。

问题是File 包含一个std::fstream 成员,它本身有一个已删除的复制构造函数。它不能被复制。 std::fstream 表示可能打开的文件。 “复制”一个打开的文件的概念是完全没有意义的,所以文件流对象不能被复制,因为File本身不能被复制,因为它包含一个不可复制的类成员,这就是编译错误。试图复制一个不可复制的类。

省略 return 语句会导致未定义的行为,即声明为返回 File 的函数不返回任何内容。

这完全是因为您在实现赋值运算符时存在几个基本问​​题。赋值运算符应该:

  • 取一个const参数。

  • 返回一个引用到它自己的实例,并且

  • 返回*this

为了实现具有预期语义的赋值运算符,应将其声明为

File &operator=(const File &a)

并通过返回完成其工作

    return *this;

【讨论】:

  • “‘复制’一个打开的文件的概念完全没有意义”——我可以看到复制文件句柄 (dup()) 的论点在语义上复制 fstream 的有效解释。所以“无意义”这个词可能太强了。尽管如此,该功能并未提供——无论如何,通过复制ctor...
  • 所以我尝试将参数设为 const,但编译器会抛出一个错误,提示我正在丢弃限定符。但是,在代码内部,const 输入是一个 R 值,因此不应更改。一旦可以更改 R 值(使用第二个 = 符号),则应关闭该函数,而不必担心 const 指标。
  • 所有其他方法,getDrive() 等。 al.,必须是 const 方法本身。因为根据定义,它们不应该修改this——它们是吸气剂,仅此而已——它们本身必须是const方法。避免错误的最佳方法是以大多数错误导致立即编译失败的方式编写代码,而不是最终浪费大量时间试图弄清楚的奇怪的运行时行为。而且,作为额外的奖励,C++ 语言的所有标准功能,例如复制构造函数,都将自动正常工作。
【解决方案2】:

您的复制操作员的签名应该是:

File & operator=(File const &);

编译器抱怨return a;,因为运算符声明它返回一个对象,而不是引用——所以a 需要被复制。但是mainFile 是一个std::fstream,它不提供自己的复制构造函数,因此编译器无法为您生成复制构造函数。您自己没有提供复制构造函数,因此 File 对象是不可复制的。这就是返回 a 时出现错误的原因。

正确的实现是:

File & operator=(File const & a) {
    this->getDrive() = a.getDrive();
    this->getFolder() = a.getFolder();
    this->getFileName() = a.getFileName();
    this->getText() = a.getText();
    this->fileName = a.fileName;
    return *this;
}

请注意,我们还通过 const 引用获取参数;这允许从 const 的 File 对象进行复制分配(否则,您不能)。此外,赋值运算符通常应该返回对赋值目标的引用(这是*this,而不是a)。

【讨论】:

  • 在复制任何内容之前不要忘记检查&amp;a != thisFile&amp; operator=(File const &amp;a) { if (&amp;a != this) { ... } return *this; }
  • 好的。所以我把通过引用指示器放在返回值中,它工作正常。但这不是意味着该函数可以访问文件在内存中的位置吗?
  • @RaymondIacobacci 这是正确的。 fstream 对象不能直接复制。但是,有一些解决方法。
  • @RemyLebeau 我认为这是一种反模式。将对象复制到自身是调用者的语义错误,不值得尝试针对这种情况进行优化 - 只是为了确保仍然满足后置条件。
  • @cdhowie:自我分配很少见,但并非不可能。可以将作业编写为无检查,但这通常涉及制作副本,而不管自我分配如何。如果复制操作通常比检查自分配更昂贵,那么在这些情况下可以提高性能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-14
  • 1970-01-01
  • 2011-06-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多