为具有私有枚举成员的 class 重载输出 (<<) 运算符

Overloading output (<<) operator for a class with private enum member

我很难为此 class 尝试重载我的 << 输出运算符。 class 有私人成员 - enum classstring。我编写的代码编译并运行,但 << 运算符仅针对枚举 class 重载。 根据我的理解和阅读,我需要做这样的事情:

friend ostream& operator<<(ostream& os, CDirectory const& dir)

但我似乎无法实现它,在这一点上我真的没有想法。我想重载整个 class CDirectory 的运算符,而不仅仅是 enum class Filetype,它是成员之一。这甚至可以做到吗?

class CDirectory {
    string m_strDirectory;
    enum class Filetype {
        Archive, Hidden, ReadOnly, System, FileNotSupported
    };
    multimap <CFile, Filetype> m_DirectoryMap;
public:
    /* overloading operator<< for the enum class Filetype */
    friend ostream& operator<<(ostream& os, Filetype const type)
    {
        switch (type)
        {
        case Filetype::Archive:
            os << "archive";
            break;
        case Filetype::Hidden:
            os << "hidden";
            break;
        case Filetype::ReadOnly:
            os << "read-only";
            break;
        case Filetype::System:
            os << "system";
            break;
        case Filetype::FileNotSupported:
            os << "not-supported";
            break;
        }
        return os;
    }
    /* explicit constructor - reads data from a file and inserts pairs
      of types pair <CFile, enum Filetype> in a multimap */
    CDirectory (const string& n) {
        fp.open (n, ios::in);
        string dirName, fileName,  fType;
        int fileSize;
        Filetype filetype;
        fp >> dirName;
        m_strDirectory = dirName;
        while (fp >> fileName >> fileSize >> fType) {
            CFile obj (fileName, fileSize);
            if (fType == "Archive")
                filetype = Filetype::Archive;
            else if (fType == "Hidden")
                filetype = Filetype::Hidden;
            else if (fType == "ReadOnly")
                filetype = Filetype::ReadOnly;
            else if (fType == "System")
                filetype = Filetype::System;
            else
                filetype = Filetype::FileNotSupported;
            m_DirectoryMap.insert(pair<CFile, Filetype>(CFile(obj.getFileName(), obj.getFileSize()), Filetype(filetype)));
        }
        auto p = m_DirectoryMap.begin();
        cout << m_strDirectory << endl;
        while ( p != m_DirectoryMap.end()) {
            cout << endl << p->first.getFileName() << '\t' << p->first.getFileSize() << '\t' << p->second << endl;
            ++p;
        }
    }
    void printMap () {
         auto p = m_DirectoryMap.begin();
         cout << m_strDirectory << endl;
         while ( p != m_DirectoryMap.end()) {
                cout << endl << p->first.getFileName() << '\t' << p->first.getFileSize() << '\t' << p->second << endl;
                ++p;
        }
    }
};

更新:

我尝试了以下代码,为我的整个 class 实现重载运算符 <<,但它产生了一个错误 - error: cannot bind 'std::basic_ostream<char>' lvalue to 'std::basic_ostream<char>&&'| 在这些行上(其中 1 个在我的构造函数中并且1 在我的 printMap 函数中)- cout << endl << p->first.getFileName() << '\t' << p->first.getFileSize() << '\t' << p->second << endl; 我猜它与 p->second 部分有关,但我不知道到底发生了什么。有什么想法吗?

更新:是的,问题是当我尝试使用 p->second 打印枚举时。如果我删除它编译的那部分..

std::ostream& operator<<(std::ostream &os, const CDirectory &dir)
    {
        const Filetype& type;
        switch (type)
        {
        case Filetype::Archive:
            os << "archive";
            break;
        case Filetype::Hidden:
            os << "hidden";
            break;
        case Filetype::ReadOnly:
            os << "read-only";
            break;
        case Filetype::System:
            os << "system";
            break;
        case Filetype::FileNotSupported:
            os << "not-supported";
            break;
        }
        os << dir.m_strDirectory << "\n";
        for(auto &p : dir.m_DirectoryMap) {
           os << p.first.getFileName() << '\t' << p.first.getFileSize() << '\t' << p.second << '\n';
        }
        return os;
    }

我的印象是您混淆了 class 的数据成员和嵌套类型(成员类型)。运算符(将)定义为类型,而不是数据成员。

您必须为 每个 class/type 重载 operator<<(用于流输出)已经定义。从你的代码来看,我认为你只需要重载

ostream& operator<<(ostream& os, Filetype const type)

你做到了。所以不清楚你的问题到底是什么,它的症状是什么。

在我看来你拥有你需要的一切,你只需要把它们放在一起。

printMap() 函数已经打印了 class CDirectory 中的所有内容。它将所有内容打印到 cout。因此,您只需执行以下操作,它就会起作用。

friend ostream& operator<<(ostream& os, const CDirectory& dir)
{
    dir.printMap();
}

但是这段代码写得不好,因为 opeartor<< 更通用,而不仅仅是为 cout 编写的。为了使它更好,您可以重写 printMap() 函数以引用要输出到的 ostream

void printMap (ostream& os) 
{
     os << m_strDirectory << endl;

     auto p = m_DirectoryMap.begin();
     while ( p != m_DirectoryMap.end()) 
     {
            os << endl << p->first.getFileName() << '\t' << p->first.getFileSize() << '\t' << p->second << endl;
            ++p;
     }
}

void printMap () { printMap(std::cout); }

现在运算符看起来像这样:

friend ostream& operator<<(ostream& os, const CDirectory& dir)
{
    dir.printMap(os);
}

我希望这能更好地解释发生了什么。

我假设你最终希望能够做这样的事情:

CDirectory dir("input_file");
std::cout << dir;

这应该会立即输出所有内容(基本上 CDirectory::printMap() 当前打印的所有内容,对吗?

为此,您需要为 CDirectory class 重载 operator<< 作为非成员函数:

std::ostream& operator<<(std::ostream &os, const CDirectory &dir)
{
    os << dir.m_strDirectory << "\n";
    for(auto &p : dir.m_DirectoryMap) {
        std::cout << p.first.getFileName() << '\t' << p.first.getFileSize() << '\t' << p.second << '\n';
    }

    return os;
}

然后让这个函数成为你的朋友 class:

friend std::ostream& operator<<(std::ostream &os, const CDirectory &dir);

使其能够访问 class 的私有成员。

或者,因为您的 class 中已经有一个打印功能,您可以转发到那个功能,这样您就不需要与 operator<< 成为朋友(有些人不喜欢朋友职能)。为此,您必须稍微更改 printMap() 函数:

void printMap (std::ostream &os) const
{
     auto p = m_DirectoryMap.begin();
     os << m_strDirectory << endl;
     while ( p != m_DirectoryMap.end()) {
            os << endl << p->first.getFileName() << '\t' << p->first.getFileSize() << '\t' << p->second << endl;
            ++p;
    }
}

std::ostream& operator<<(std::ostream &os, const CDirectory &dir)
{
    dir.printMap();
    return os;
}

一旦你有了这个运算符,用两种变体中的任何一种编写,在你的 main() 函数中你可以写:

CDirectory dir("input_file");
std::cout << dir;

关于您已经为 Filetype 重载的运算符的两个注释。它是唯一需要为您的 class 重载的其他运算符才能流式传输(operator<< 已经为 std::string 和整数类型重载,因此您不必这样做能够写字符串和数字)。还要记住,即使你把它写在你的 class 中,它实际上不是一个成员函数,也不能是一个成员函数(为什么?)。您可以在那里定义它,因为您声明了它 friend,但是通常定义友元函数的方式是这样的:

class A {
    ...
    friend void f();
    ...
};

void f() {
    ...
}