题目信息

最近遇到了一个题目比较棘手,要实现一个名为 Log 的类,能方便输出调试信息,有要求如下:

  • 支持使用流运算符输出信息,即:

    Log obj;
    obj<<"DEBUG MESSAGE"<<endl;
  • 支持对调试信息进行分级控制,在程序过程中可以随意改变调试级别,如果调试级别高于某个信息的输出级别,则禁止该消息输出,反之则输出该信息,例如:

    obj.set_level("error"); // 设置为 error 级别
    obj<<level("warning")<<"WARNING MESSAGE"<<endl; // 不进行输出
    obj<<level("fatal")<<"BROKEN"<<endl; // 进行输出

    级别权重: fatal > error > warning

代码思路

首先,头文件使用一般常见的头文件带上 string.h 就行,然后我们需要先定义一个 LogClass 嘛,我们这里可以使用两个不同的变量分别储存现在的调试级别以及即将输出的信息级别,那我们这里使用的分别是 statusnow 。然后我们就可以先实现最简单的函数 set_level,基本上就是使用字符串比较然后更新 status 也就是调试级别。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>

using namespace std;

class Log {
private:
    int status, now
public:
    void set_level(const char* s) {
        if(strcmp(s, "warning") == 0) status = 1;
        if(strcmp(s, "error") == 0) status = 2;
        if(strcmp(s, "fatal") == 0) status = 3;
        return ;
    }
};

int main() {
    return 0;
}

好,接下来我们弄一下初始化,然后我们就先来实现基本的流运算符重载好了,最起码先把信息输出出去嘛。这里我们还没有弄等级控制,之后才加上去也不迟。然后我们为了可以连续输出信息,所以我们要返回 *this

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>

using namespace std;

class Log {
private:
    int status, now
public:
    Log() : status(1), now(1) {}
    void set_level(const char* s) {
        if(strcmp(s, "warning") == 0) status = 1;
        if(strcmp(s, "error") == 0) status = 2;
        if(strcmp(s, "fatal") == 0) status = 3;
        return ;
    }
    
    Log& operator<< (const char* s) {
        cout<<s;
        return *this;
    }
};

int main() {
    Log obj;
    obj<<"DEBUG_MESSAGE"<<endl;
    return 0;
}

但是当我们想要编译这个代码的时候,我们发现居然是 endl 的部分出现了问题(如果代码没错的话),因为 endl 其实并不是简单的换行符,endl其实是一个函数,详细有兴趣的可以去查一下文档,我们这里直接上代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>

using namespace std;

class Log {
private:
    int status, now
public:
    Log() : status(1), now(1) {}
    void set_level(const char* s) {
        if(strcmp(s, "warning") == 0) status = 1;
        if(strcmp(s, "error") == 0) status = 2;
        if(strcmp(s, "fatal") == 0) status = 3;
        return ;
    }
    
    Log& operator<< (const char* s) {
        cout<<s;
        return *this;
    }
    
    Log& operator<< (ostream& (*op)(ostream&)) {
        cout<<endl;
        return *this;
    }
};

int main() {
    Log obj;
    obj<<"DEBUG_MESSAGE"<<endl;
    return 0;
}

如果代码没出现错误的话,到这里我们就可以看到我们的第一条信息成功输出了,接下来我们就来实现我们的等级控制,首先我们先把我们的 level 函数弄一下,其实是跟 set_level 差不多的思路。然后我们再弄一下流运算符接收的部分:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>

using namespace std;

class Log {
private:
    int status, now
public:
    Log() : status(1), now(1) {}
    void set_level(const char* s) {
        if(strcmp(s, "warning") == 0) status = 1;
        if(strcmp(s, "error") == 0) status = 2;
        if(strcmp(s, "fatal") == 0) status = 3;
        return ;
    }
    
    // 输出调试信息
    Log& operator<< (const char* s) {
        cout<<s;
        return *this;
    }
    
    // 接收等级信息
    Log& operator<< (int i) {
        now = i;
        return *this;
    }
    
    // 输出 endl
    Log& operator<< (ostream& (*op)(ostream&)) {
        cout<<endl;
        return *this;
    }
};

int level(const char* s) {
    if(strcmp(s, "warning") == 0) return 1;
    if(strcmp(s, "error") == 0) return 2;
    if(strcmp(s, "fatal") == 0) return 3;
    return 1;
}

int main() {
    Log obj;
    obj<<"DEBUG_MESSAGE"<<endl;
    return 0;
}

其实接下来我们把等级控制加上去就可以完成了,就是一个 if 判断的功夫而已,但是我们注意到这里我们只能输出文字信息,那如果我们要输出 int 或者其他类型的信息,是不能进行输出的,而且还有可能报错,因为我们接收等级信息的方式是使用 int 来传递的,这就造成了如果要输出 int 信息,将会无法输出乃至出错,所以我们这里可以多定义一个叫 LogLevel 的 Class 来进行等级传递,顺便加上等级控制:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>

using namespace std;

class LogLevel {
public:
    int level; // 放 public 方便调取
    LogLevel() : level(1) {} // 如果默认创建的话就是 warning 等级
    LogLevle(int i) : level(i) {} // 如果有传入等级的话就设定一下
};

class Log {
private:
    int status, now
public:
    Log() : status(1), now(1) {}
    void set_level(const char* s) {
        if(strcmp(s, "warning") == 0) status = 1;
        if(strcmp(s, "error") == 0) status = 2;
        if(strcmp(s, "fatal") == 0) status = 3;
        return ;
    }
    
    // 输出调试信息
    Log& operator<< (const char* s) {
        if(now >= status) {
            cout<<s;
        }
        return *this;
    }
    
    // 接收等级信息,使用 LogLevel
    Log& operator<< (LogLevel* src) {
        now = src->level;
        delete src;
        return *this;
    }
    
    // 输出 endl
    Log& operator<< (ostream& (*op)(ostream&)) {
        if(now >= status) {
            cout<<endl;
        }
        return *this;
    }
};

LogLevel* level(const char* s) {
    if(strcmp(s, "warning") == 0) return new LogLevel(1);
    if(strcmp(s, "error") == 0) return new LogLevel(2);
    if(strcmp(s, "fatal") == 0) return new LogLevel(3);
    return new LogLevel(1);
}

int main() {
    Log obj;
    obj<<"DEBUG_MESSAGE"<<endl;
    obj.set_level("error");
    obj<<level("warning")<<"WARNING MESSAGE"<<endl;
    obj<<level("fatal")<<"BROKEN"<<endl;
    return 0;
}

但是目前我们还是只能输出字符串信息,我们这里可以使用模板来进行处理,加上这个就是我们完整的代码了:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>

using namespace std;

class LogLevel {
public:
    int level; // 放 public 方便调取
    LogLevel() : level(1) {} // 如果默认创建的话就是 warning 等级
    LogLevel(int i) : level(i) {} // 如果有传入等级的话就设定一下
};

class Log {
private:
    int status, now;
public:
    Log() : status(1), now(1) {}
    void set_level(const char* s) {
        if(strcmp(s, "warning") == 0) status = 1;
        if(strcmp(s, "error") == 0) status = 2;
        if(strcmp(s, "fatal") == 0) status = 3;
        return ;
    }
    
    // 输出调试信息
    template <typename T>
    Log& operator<< (T s) {
        if(now >= status) {
            cout<<s;
        }
        return *this;
    }
    
    // 接收等级信息,使用 LogLevel
    Log& operator<< (LogLevel* src) {
        now = src->level;
        delete src;
        return *this;
    }
    
    // 输出 endl
    Log& operator<< (ostream& (*op)(ostream&)) {
        if(now >= status) {
            cout<<endl;
        }
        return *this;
    }
};

LogLevel* level(const char* s) {
    if(strcmp(s, "warning") == 0) return new LogLevel(1);
    if(strcmp(s, "error") == 0) return new LogLevel(2);
    if(strcmp(s, "fatal") == 0) return new LogLevel(3);
    return new LogLevel(1);
}

int main() {
    Log obj;
    obj<<"DEBUG_MESSAGE"<<endl;
    obj.set_level("error");
    obj<<level("warning")<<"WARNING MESSAGE"<<endl;
    obj<<level("fatal")<<"BROKEN"<<endl;
    return 0;
}