假设模块A对外提供一个类CPerson
class CPerson
{
public:
std::string m_strName;
int m_nAge = 0;
};
类CPerson定义三个字段:名字、年龄。
假设模块B集成模块A的能力,模块A内部和模块B都会使用类CPerson,那么会存在什么问题呢?
类CPerson的名字字段是使用std::string,std::string的定义和实现跟运行时库有关,vs2013和vs2017的运行时库可能是不一样的。如果模块A使用vs2017开发,而模块B使用vs2013开发,类Person对模块A和模块B来说是不一样的,必然会出现各种问题,甚至崩溃。那怎么办呢?
屏蔽CPerson内部的结构,对外导出方法。
class CPerson
{
public:
const char* GetName() { return m_strName.c_str(); }
void SetName(const char* name) { m_strName = name; }
int GetAge() { return m_nAge; }
void SetAge(int nAge) { m_nAge = nAge; }
private:
std::string m_strName;
int m_nAge = 0;
};
对外导出的方法使用的int、const char*属于基本类型,与运行时库无关,解决了对运行时库的依赖,那还有其它隐患吗?
有,虽然导出的方法的实现都是在模块A内部,对内部字段的读写完全是由模块A实现,但是实例化CPerson对象的时候,给CPerson对象分配空间的大小sizeof(CPerson)跟CPerson内部字段的定义还是有关,假设模块B使用vs2013开发分配的CPerson对象的大小,比模块A使用vs2017开发分配的CPerson对象的大小来得小,就会导致调用SetAge()方法时越界内存破坏。那怎么办?
强烈建议不要在对外的类中使用std::string,std::vector等与运行时库相关的类。如果一定要用,那就另外提供CPerson类的创建/释放接口。
假设为了提升对CPerson的访问性能,于是在对外导出的方法中加上关键字inline,会有什么问题?
class CPerson
{
public:
inline const char* GetName() { return m_strName.c_str(); }
inline void SetName(const char* name) { m_strName = name; }
inline int GetAge() { return m_nAge; }
inline void SetAge(int nAge) { m_nAge = nAge; }
private:
std::string m_strName;
int m_nAge = 0;
};
关键字inline会使得对外方法的实现代码直接编译进调用模块B内,等于将CPerson内部的字段细节暴露给使用者,跟直接将字段定义成public没有区别,也会导致模块A和模块B在使用不同的运行时库时存在问题。