C++“接口”与“实现”分离的两种方法

原文链接:C++“接口“与“实现“分离的两种方法_接口与实现分离-CSDN博客

两种方法:

1、Pimpl Idiom(pointer to implementation),将对象的实现细目隐藏于指针背后,简单的说就是将其分成两个类,一个类只提供接口,另一个负责实现该接口。

2、Object Interface,将接口定义为抽象类,接口全被定义为纯虚函数(纯虚函数没有具体的实现方法),派生类的成员函数负责实现这些接口。千万不要忘记把抽象接口类的析构函数定义为virtual函数,可能会造成内存泄漏。

Pimpl Idiom手法

举个简单的例子,要求实现一个Person接口,其要包含如下四个函数:
string& getName() const;   //获取名字
void setName(string& name);  //设置名字
int getAge() const;  //获取年龄
void setAge(int age);  //设置年龄

按正常思路编写一个Peronimpl类来实现这些函数,Peronimpl.h代码如下:

#include<string>
#include <iostream>
using namespace std;

class PersonImpl {
public:
    PersonImpl(string& name, int age);
    virtual ~PersonImpl();

    string& getName() const;
    void setName(string& name);
    int getAge() const;
    void setAge(int age);

private:
    string& mName;
    int mAge;
};

Personimpl.cpp代码如下:

PersonImpl::PersonImpl(string& name, int age):
    mName(name),
    mAge(age){}

PersonImpl::~PersonImpl() {}

string& PersonImpl::getName() const {
    return mName;
}

void PersonImpl::setName(string& name) {
    mName = name;
}

int PersonImpl::getAge() const {
    return mAge;
}

void PersonImpl::setAge(int age) {
    mAge = age;
}

此时,如果直接给用户提供Personimpl类,则需要将头文件提供给用户,用户创建对象后,不仅可以看到函数的具体实现细节,还可以看到我们的私有成员,哒咩!!!因此,我们可以在原本的类上再套一个接口,实现一些隐藏目的。

接口类为Person,函数声明在Person.h文件中,具体如下:

class PersonImpl;
using namespace std;

class Person {
public:
    Person(std::string& name, int age);
    virtual ~Person();

    string& getName() const;
    void setName(std::string& name);
    int getAge() const;
    void setAge(int age);

private:
    PersonImpl *mPersonImpl;
};

接口类的头文件尽量不要添加#include<>,要添加具体实现类PersonImpl的声明,public中声明PersonImpl类中的函数,private中加入PersonImpl类的指针。

Person.cpp代码如下:

#include "Person.h"
#include "PersonImpl.h"

Person::Person(std::string& name, int age):
    mPersonImpl(new PersonImpl(name, age))
{
    std::cout << "construct Person" << std::endl;
}

Person::~Person() {
    delete mPersonImpl;
    std::cout << "deconstruct Person" << std::endl;
}

string& Person::getName() const {
    return mPersonImpl->getName();
}

void Person::setName(std::string& name) {
    mPersonImpl->setName(name);
}

int Person::getAge() const {
    return mPersonImpl->getAge();
}

void Person::setAge(int age) {
    mPersonImpl->setAge(age);
}

此时,一个接口类Person就完成了。

Object Interface手法

同样我们参照上面那个例子,再实现一个Animal接口,依旧是下面四个函数:
string& getName() const;   //获取名字
void setName(string& name);  //设置名字
int getAge() const;  //获取年龄
void setAge(int age);  //设置年龄

不同于PImpl手法,我们首先编写的是接口类,起名为Animal,Animal.h如下:

using namespace std;

class Animal {
public:
    Animal(){};
    virtual ~Animal(){};

    virtual string& getName() const = 0;
    virtual void setName(std::string& name) = 0;
    virtual int getAge() const = 0;
    virtual void setAge(int age) = 0;
};

Animal* creat(std::string& name, int age);

接口类中将函数全声明为纯虚函数 ,此时接口类为抽象类,不能创建对象!

创建一个RealAnimal类继承Aniaml类,实现具体函数功能,RealAnimal.h代码如下:

#include "Animal.h"

class RealAnimal: public Animal {
public:
    RealAnimal(string& name, int age);
    virtual ~RealAnimal();

    string& getName() const;
    void setName(string& name);
    int getAge() const;
    void setAge(int age);

private:
    friend Animal* creat(string& name, int age);

private:
    string& mName;
    int mAge;
};

在RealAnimal类中,除了继承的接口函数的声明之外,还多了一个友元函数,其作用就是实例化一个对象,并且可以访问该类的私有成员。下面看一下接口真正的实现细节,具体如下:

#include "RealAnimal.h"

RealAnimal::RealAnimal(string& name, int age):
    mName(name),
    mAge(age){}

RealAnimal::~RealAnimal(){}

string& RealAnimal::getName() const {
    return mName;
}

void RealAnimal::setName(string& name){
    mName = name;
}

int RealAnimal::getAge() const{
    return mAge;
}

void RealAnimal::setAge(int age){
    mAge = age;
}

Animal* creat(string& name, int age) {
    return new RealAnimal(name, age);
}

Animal* creat(string& name, int age)确实只是实例化一个RealAnimal对象,返回的却是Animal接口对象,所以必须将类Animal 的析构函数声明为虚函数,不然会造成内存泄漏。

存在父类指针指向子类对象时,父类指针无法释放子类对象----》需要将父类析构函数写成虚析构或者纯虚析构!

使用:

#include "Animal.h"
#include<iostream>
using namespace std;

int main()
{
	string name{ "pigkai" };
	Animal* animal = creat(name,10);
	animal->setAge(20);

	cout << animal->getName() << " " << animal->getAge() << endl;
	delete animal;  //***
	system("pause");
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yy_xzz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值