C++降低编译依赖—Pimpl和虚基类
文章目录
方法一:声明和实现分离
这当然是最容易实现的方式,把代码分为.cpp
和.h
文件
方法二:Pimpl(Pointer to Implementation)
通过一个私有的成员指针或者引用,将指针所或者引用指向的类的内部实现和数据进行隐藏
目录结构:
Pimpl代码实现:
person类把具体实现交给realperson类
//person.h
#ifndef PERSON_H
#define PERSON_H
#include <memory>
#include <string>
class RealPerson;
class Person
{
private:
std::shared_ptr<RealPerson> pImpl;
public:
void hello() const;
public:
Person();
Person(const std::string &name);
~Person();
};
#endif /* PERSON_H */
//person.h
#ifndef PERSON_H
#define PERSON_H
#include <memory>
#include <string>
class RealPerson;
class Person
{
private:
std::shared_ptr<RealPerson> pImpl;
public:
void hello() const;
public:
Person();
Person(const std::string &name);
~Person();
};
#endif /* PERSON_H */
//person.cpp,注意要包含下面的头文件
#include "realperson.h"
#include "person.h"
Person::Person() : pImpl(new RealPerson()) {}
Person::Person(const std::string &name) : pImpl(new RealPerson(name)) {}
Person::~Person() {}
void Person::hello() const
{
pImpl->hello();
}
//realperson.h
#ifndef REALPERSON_H
#define REALPERSON_H
#include <string>
#include <iostream>
#include "person.h"
class RealPerson
{
private:
std::string name;
public:
void hello() const;
public:
RealPerson();
RealPerson(std::string name);
~RealPerson();
};
#endif /* REALPERSON_H */
//realperson.cpp
#include "realperson.h"
RealPerson::RealPerson(): name("") {}
RealPerson::RealPerson(std::string name): name(name) {}
RealPerson::~RealPerson() {}
void RealPerson::hello() const
{
std::cout << "Hi I'm " << name << std::endl;
}
//main.cpp
#include "person.h"
int main()
{
Person JJ("JJ");
JJ.hello();
return 0;
}
最后给出Makefile
CC := g++
C_FLAGS := -std=c++17 -Wall -Wextra
BIN := bin
SRC := src
INCLUDE := include
LIB := lib
LIBRARIES :=
EXECUTABLE := main
all: $(BIN)/$(EXECUTABLE)
clean:
$(RM) $(BIN)/$(EXECUTABLE)
run: all
./$(BIN)/$(EXECUTABLE)
$(BIN)/$(EXECUTABLE): $(SRC)/*
$(CC) $(C_FLAGS) -I$(INCLUDE) -L$(LIB) $^ -o $@ $(LIBRARIES)
方法三:抽象类
抽象类利用工厂函数,把具体实现交给派生类
目录结构
代码
//person.h
#ifndef PERSON_H
#define PERSON_H
#include <memory>
#include <string>
class Male;
class Female;
class Person
{
public:
static std::shared_ptr<Person> create(std::string sex);
//可以有更多create或其它函数...
public:
virtual void hello() = 0;
public:
Person();
virtual ~Person();
};
#endif /* PERSON_H */
//person.cpp
#include "person.h"
#include "male.h"
#include "female.h"
Person::Person() {}
Person::~Person() {}
std::shared_ptr<Person> Person::create(std::string sex)
{
if(sex == "Male")
return std::shared_ptr<Person> (new Male());
return std::shared_ptr<Person> (new Female());
}
//female.h
#ifndef FEMALE_H
#define FEMALE_H
#include "person.h"
class Female: public Person
{
private:
std::string sex = "female";
public:
virtual void hello();
public:
Female();
virtual ~Female();
};
#endif /* FEMALE_H */
//female.cpp
#include "female.h"
#include <iostream>
Female::Female() {}
Female::~Female() {}
void Female::hello()
{
std::cout << "Hi I'm " << this->sex << std::endl;
}
//male.h
#ifndef MALE_H
#define MALE_H
#include "person.h"
class Male : public Person
{
private:
std::string sex = "male";
public:
virtual void hello();
public:
Male();
virtual ~Male();
};
#endif /* MALE_H */
//male.cpp
#include "male.h"
#include <iostream>
Male::Male() {}
Male::~Male() {}
void Male::hello()
{
std::cout << "Hi I'm " << this->sex << std::endl;
}
//main.cpp
#include "person.h"
int main()
{
auto p = Person::create("male");
p->hello();
return 0;
}
最后给出Makefile
CC := g++
C_FLAGS := -std=c++17 -Wall -Wextra
BIN := bin
SRC := src
INCLUDE := include
LIB := lib
LIBRARIES :=
EXECUTABLE := main
all: $(BIN)/$(EXECUTABLE)
clean:
$(RM) $(BIN)/$(EXECUTABLE)
run: all
./$(BIN)/$(EXECUTABLE)
$(BIN)/$(EXECUTABLE): $(SRC)/*
$(CC) $(C_FLAGS) -I$(INCLUDE) -L$(LIB) $^ -o $@ $(LIBRARIES)
总结
- PImpl模式优点:降低模块的耦合、降低编译依赖,提高编译速度、接口与实现分离,提高接口的稳定性;在接口不变的情况下可以修改实现,在动态库中很有用,可以直接增加新的接口而不影响二进制兼容性,不影响用户使用
- 抽象接口同样可以:降低模块的耦合、降低编译依赖;但是在动态库中增加新的接口会影响二进制兼容性,因为虚函数表要改变