背景
- 本文的源码来自于Amazon的AVS C++ SDK,AVS C++ SDK 源码链接
- 本文具体分析文件的路径:
文件名称 | 文件路径 |
---|---|
Logger.h/ConsoleLogger.h | avs-device-sdk/AVSCommon/Utils/include/AVSCommon/Utils/Logger |
Logger.cpp/ConsoleLogger.c | AVSCommon/Utils/src/Logger |
- 本文只是针对多态这一C++特性在这里的应用,对于其它内容的代码会在Logger完整分析的文章中进行详细说明
源码分析
Logger 基类
Logger.h
class Logger {
public:
...
/**
* Send a log entry to this Logger.
*
* @param level The severity Level to associate with this log entry.
* @param entry Object used to build the text of this log entry.
*/
void log(Level level, const LogEntry& entry);
...
/**
* Emit a log entry.
* NOTE: This method must be thread-safe.
* NOTE: Delays in returning from this method may hold up calls to Logger::log().
*
* @param level The severity Level of this log line.
* @param time The time that the event to log occurred.
* @param threadMoniker Moniker of the thread that generated the event.
* @param text The text of the entry to log.
*/
virtual void emit(
Level level,
std::chrono::system_clock::time_point time,
const char* threadMoniker,
const char* text) = 0;
上面代码只是摘取了Logger.h 中与本文章需要讲解的多态应用的部分代码.
在Logger.h 的Logger类声明中,将emit修饰为了纯虚函数(在声明后加了代码=0).
Logger.c
...
void Logger::log(Level level, const LogEntry& entry) {
if (shouldLog(level)) {
emit(level, std::chrono::system_clock::now(), ThreadMoniker::getThisThreadMoniker().c_str(), entry.c_str());
}
}
...
而在Logger.c 中,Logger::log 又调用了emit方法. 而emit在Logger中是定义为了虚函数,这样可行?
实际上,在Logger类的声明中将emit修饰为了纯虚函数,代表了Logger类不能直接实例化对象, 需要被子类继承并由子类实现emit方法.
ConsoleLgger子类
ConsoleLgger.h
/**
* A very simple (e.g. not asynchronous) @c Logger that logs to console.
* * Inheriting @c std::ios_base::Init ensures that the standard iostreams objects are properly initialized before @c
* ConsoleLogger uses them.
*/
class ConsoleLogger
: public Logger
, private std::ios_base::Init {
public:
/**
* Return the one and only @c ConsoleLogger instance.
*
* @return The one and only @c ConsoleLogger instance.
*/
static std::shared_ptr<Logger> instance();
...
void emit(Level level, std::chrono::system_clock::time_point time, const char* threadMoniker, const char* text)
override;
在ConsoleLgger的声明中首先继承了Logger基类,也就是在ConsoleLogger中需要实现Logger基类中的emit方法.
在上面截取的代码片段中声明了emit方法,在声明父类中声明的纯虚函数需要注意以下两点:
- 子类声明父类的纯虚函数,格式和名称要和父类完整一直
- 在声明后,需要加上override进行修饰,告诉编译器该方法是重写
ConsoleLgger.c
void ConsoleLogger::emit(
Level level,
std::chrono::system_clock::time_point time,
const char* threadMoniker,
const char* text) {
if (m_coutMutex) {
std::lock_guard<std::mutex> lock(*m_coutMutex);
std::cout << m_logFormatter.format(level, time, threadMoniker, text) << std::endl;
}
}
在ConsoleLogger.c中实现emit方法.
在Logger.h 还定义以下的内敛函数
/**
* Inline method to get the function that ACSDK_<LEVEL> macros will send logs to.
* In this case @c ACSDK_LOG_MODULE was not defined, so logs are sent to the @c Logger returned by
* @c get<ACSDK_LOG_SINK>Logger().
*/
inline Logger& ACSDK_GET_LOGGER_FUNCTION() {
static std::shared_ptr<Logger> logger = ACSDK_GET_SINK_LOGGER();
/*而ACSDK_GET_SINK_LOGGER()实质上是调用了ConsoleLogger中的getConsoleLogger()进行ConsoleLogger对象的获取.*/
return *logger;
}
上述代码片段展示的是将子类ConsoleLogger的实例化对象赋值给父类Logger定义的指针变量.
而当通过logger调用emit时,实质上调用的子类Logger实现的emit函数.
...
void Logger::log(Level level, const LogEntry& entry) {
if (shouldLog(level)) {
emit(level, std::chrono::system_clock::now(), ThreadMoniker::getThisThreadMoniker().c_str(), entry.c_str());
}
}
...
也就是当调用logger.log,实质上logger::log()中调用的emit实质上是ACSDK_GET_SINK_LOGGER()中获取的实例(子类)中的emit.
而上面展示的过程实质就是C++所说的多态特性,不同的子类赋值给父类定义的指针变量,然后通过父类进行在不同子类实现的虚函数,实现对不同的子类实现虚函数调用,从而实现多态,也就是调用父类中定义的虚函数,会基于赋值的不同子类而实现不同的动作(每一个子类中国的虚函数中实现的内容可能不同).
#### 总结
本文简单描述了下多态在AVS C++ SDK Utils Logger中的应用.多态的应用增加了代码的可拓展想,可以通过父类对子类对象的方法实现调用. 从而实现代码中调用同一个方法而实现不同的效果.