C++中的pimpl用法(Pointer to Implementation)

定义

一种C++的编程手法,一方面可以减少编译依赖,对于大工程来说可以减少编译时间,一方面对于库来说可以减少私有成员的暴露,减少风险。

demo

// student.h
class Student
{
	Student(int age, string name, int sex);
	~Student();
	string show();
private:
	int m_age;
	string m_name;
	int m_sex;
}
// student.cpp
string Student::show(){return m_name;};
Student::Student(int age, string name, int sex):m_age(age),\
m_name(name), m_sex(sex){};

上面是一个学生类的定义和实现,现在如果想用这个学生类的有school.h , teacher.h, parent.h三个头文件使用,我们可以看到,一旦动了学生类,那么依赖的这三个类都要重新编译。

优化

// student.h
class StudentImpl;
class Student
{
	Student(int age, string name, int sex);
	~Student();
	string show();
private:
	shared_ptr<StudentImpl> m_student;
 }
//student_impl.h
class StudentImpl
{
StudentImpl(int age, string name, int sex);
~StudentImpl();
string name(){return m_name;};

private:
	int m_age;
	string m_name;
	int m_sex;
}
//student_impl.cpp
StudentImpl::StudentImpl(int age, string name, int sex):m_age(age),m_name(name), m_sex(sex){};

// student.cpp
#include "student_impl.h"
#include "student.h"
Student::Student(int age, string name, int sex)
{
   m_student = make_shared<StudentImpl>(age,name,sex);
};
string Student::show(){return student->name();};

这样的话,你就是student.cpp接口改翻天,此外你的实现也就是成员变量student_impl.h/cpp改翻天也不影响school.h , teacher.h, parent.h,因为他们理论来说编译的时候不依赖StudentImpl,这样在大型项目中,就可以加快编译速度,此外如果代码是提供对外的库比如opencv,一般会给一个头文件和so, 这里会有一个student.h,可是student.so外人压根不知道这个类的私有成员是啥,你也很难推出库函数的逻辑,保护了算法和知识产权。

paddle案例

位置:paddle/fluid/memory/allocation/thread_local_alloctor.h

class ThreadLocalAllocatorImpl;

class ThreadLocalAllocation : public Allocation {
 public:
  ThreadLocalAllocation(void* ptr, size_t size, platform::Place place)
      : Allocation(ptr, size, place) {}

  void SetThreadLocalAllocatorImpl(
      std::shared_ptr<ThreadLocalAllocatorImpl> allocator) {
    allocator_ = allocator;
  }  //给impl指针赋值

  std::shared_ptr<ThreadLocalAllocatorImpl> GetAllocator() {
    return allocator_;
  } //获取impl指针

 private:
  std::shared_ptr<ThreadLocalAllocatorImpl> allocator_;
};

class ThreadLocalAllocatorImpl
    : public std::enable_shared_from_this<ThreadLocalAllocatorImpl> {
 public:
  ThreadLocalAllocatorImpl(Place& p);
  ThreadLocalAllocation* AllocateImpl(size_t size);//不要觉得奇怪,一个类可以在自己内部声明一个指针的,这里可以理解为AllocateImpl是在ThreadLocalAllocation类中。
  void FreeImpl(ThreadLocalAllocation* allocation);//理解同上
  uint64_t ReleaseImpl();

 private:
  unique_ptr<BuddyAllocator> buddy_allocator_;//理解在ThreadLocalAllocation类中。
  Place place_;//同上
};

上面这段代码是抽取一个allocator相关代码,整个库中有非常非常多的这种,但是有个问题是ThreadLocalAllocatorImpl和ThreadLocalAllocator在一个文件中,正确的写法是ThreadLocalAllocatorImpl应该在thread_local_alloctor.cpp,如果按当下的写法一旦改变private成员,依赖这个thread_local_alloctor.h的文件都会重新编译,所以这个IMPL的意义是什么?是有什么高深的用法吗?不知道是大佬写错了还是我会错意了,希望懂得大神能留言解释一下。

### C++PIMPL 模式的实现与用途 PIMPLPointer to IMPLementation)是一种设计模式,用于隐藏类的实现细节并减少编译依赖。通过将类的具体实现封装到一个独立的对象中并通过指针访问该对象,可以在头文件中仅暴露必要的接口定义。 #### 实现方式 以下是基于 `std::unique_ptr` 的典型 PIMPL 模式实现: ```cpp // Header file: Person.h #ifndef PERSON_H #define PERSON_H #include <memory> #include <string> class Person { public: explicit Person(const std::string& name); ~Person(); void setName(const std::string& name); const std::string& getName() const; private: class Impl; std::unique_ptr<Impl> impl_; }; #endif // PERSON_H // Source file: Person.cpp #include "Person.h" #include <iostream> class Person::Impl { public: Impl(const std::string& name) : name_(name) {} ~Impl() {} void setName(const std::string& name) { name_ = name; } const std::string& getName() const { return name_; } private: std::string name_; }; Person::Person(const std::string& name) : impl_(std::make_unique<Impl>(name)) {} Person::~Person() {} void Person::setName(const std::string& name) { impl_->setName(name); } const std::string& Person::getName() const { return impl_->getName(); } ``` 在此示例中,`Person` 类的私有成员是一个指向未公开内部实现类 `Impl` 的 `std::unique_ptr`。这种分离使得用户无需知道 `Person` 的具体实现细节[^1]。 #### 使用场景 PIMPL 模式通常适用于以下情况: - 需要降低二进制兼容性风险。 - 希望减少头文件中的依赖项以加快构建时间。 - 提供稳定的 ABI 接口而不影响底层实现的变化。 尽管如此,在现代 C++ 编程实践中需要注意性能开销以及额外内存分配的影响[^2]。 #### 设计注意事项 为了保持接口稳定性同时允许子类自定义行为,建议采用模板方法模式而非直接开放虚拟函数给派生类重写[^3]。这有助于维护一致性的外部 API 定义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值