Effective C++(七):inline关键字, 降低文件间依存关系
一、知识点一
将函数声明为内联一共有两种方法,一种是为其显式指定inline关键字,另一种是直接将成员函数的定义式写在类中,如下所示:
class Person {
public:
...
int Age() const { return theAge; } // 隐式声明为 inline
...
private:
int theAge;
};
在inline诞生之初,它被当作是一种对编译器的优化建议,即将“对此函数的每一个调用”都以函数本体替换之。但在编译器的具体实现中,该行为完全被优化等级所控制,与函数是否内联无关。
在现在的 C++ 标准中,inline作为优化建议的含义已经被完全抛弃,取而代之的是“允许函数在不同编译单元中多重定义”,使得可以在头文件中直接给出函数的实现。
在 C++17 中,引入了一个新的inline用法,使静态成员变量可以在类中直接定义:
class Person {
public:
...
private:
static inline int theAge = 0; // since C++17
};
在之前的C++标准中,静态变量需要在类外部进行初始化。
二、将文件间的编译依存关系降至最低
C++ 坚持将类的实现细节放置于类的定义式中,这就意味着,即使你只改变类的实现而不改变类的接口,在构建程序时依然需要重新编译。这个问题的根源出在编译器必须在编译期间知道对象的大小,如果看不到类的定义式,就没有办法为对象分配内存。也就是说,C++ 并没有把“将接口从实现中分离”这件事做得很好。
为了更好地将类中接口的定义和实现分割开来,我们可以实现两个类,一个类负责暴露接口,一个类负责接口的具体实现:
// person.hpp 负责声明类
class PersonImpl;
class Person {
public:
Person();
void Print();
...
private:
std::shared_ptr<PersonImpl> pImpl;
};
// person.cpp 负责实现类
class PersonImpl {
public:
int data{ 0 };
};
Person::Person() {
pImpl = std::make_shared<PersonImpl>();
}
void Person::Print() {
std::cout << pImpl->data;
}
这样,假如我们要修改Person的private成员,就只需要修改PersonImpl中的内容,而PersonImpl的具体实现是被隐藏起来的,对它的任何修改都不会使得Person客户端重新编译,真正实现了“类的接口和实现分离”。在这里PersonImpl也被称作是句柄类
三、接口类
实现类额接口和实现分离的另外一种方法是接口类。也就是一个只包含若干接口的类。
class Person {
public:
virtual ~Person() {}
virtual void Print();
static std::shared_ptr<Person> Create();
};
但是此时此刻 Create 函数还无法使用,需要在派生类中给出 Person 类的函数的具体实现。
class RealPerson : public Person {
public:
RealPerson(…) {…}
virtual ~RealPerson() {}
void Print() override {…}
private:
int date{0};
};
在我们实现了RealPerson的定义以后,我们再完成 Create 函数的定义
static std::shared_ptr Person::Create() {
return std::make_shared();
}
毫无疑问的是,句柄类和接口类都需要额外的开销,句柄类通过 Person 类中的指向PersomImpl的指针来获得对象数据,增加一层间接访问,指针大小和动态分配内存带来的开销; 而接口类会增加存储虚表指针和实现虚函数跳转带来的开销。
如果这些开销太大了,就应该以具象类代替句柄类和接口类。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!