条件成员函数

Posted

技术标签:

【中文标题】条件成员函数【英文标题】:Conditional Member Functions 【发布时间】:2011-08-06 17:52:50 【问题描述】:

关于在 C++ 类中有条件地定义成员函数的建议是什么? (问题集中在限制 DLL 中某些类的外部暴露 - 特别是当这些类作为参数传入时)。显然这不是你想对数据成员做的事情,但函数应该没问题,不是吗?

例如:

class A

public:
    void func1();

#ifdef _CONDITION_
    void func2(B b);
#endif    
;

已编辑: 添加公共修饰符以避免示例混淆。

【问题讨论】:

你能详细说明你为什么要这样做吗? #indef(预处理器)是编译前的东西,所以你不能用它来使成员函数出现在某些调用中而不是其他调用中。 当使用 C++ 中的共享库 (DLL) 时,您通常仍包含在库中编译的类的头文件。在这种情况下,我不希望使用 DLL 的软件看到这些头文件中引用的某些类。 oic;您正在创建不同风格的头文件,一种用于内部使用,另一种用于外部接口?听起来你的技术会很好。如果这与您的需求相似,我已经看到 #if 控制是否发出 __declspec(dllexport) 之类的东西的情况。 @seand 这样好吗?如果 dll 和程序看到类的不同定义,它们会构建不同的内部表示,并且类型虽然具有相同的名称,但不再匹配。如果是这样的话,听起来对我来说是一个糟糕的解决方案。 @ivella 你是对的!内部结构(vtables 等)将不正常。如果这是@Nicolas-s 最初的目标,也许最好只 dllexport 打算在外部可见的函数。 【参考方案1】:

通常,如果您不想公开导出类的某些部分,那么您应该考虑不公开该类的选项,而是提供您的类继承自的抽象接口。

例如。

class AbstractExportedInterface

public:
    virtual void do_stuff() = 0;
;

class HasStuffIDontWantToExport : public AbstractExportedInterface

public:
    void do_stuff();
    void do_other_stuff_that_i_dont_export();
;

那么您将假设您正在向 DLL 用户提供 HasStuffIDontWantToExport* 并且他们只有 AbstractExportedInterface 的标头。

编辑:对第一条评论的回应

如果您希望您的 DLL 客户端能够以某种方式使用某些类型(第 3 方或其他类型),但您不希望他们拥有对这些类型的完全访问权限,并且您没有灵活性使用直接继承层次结构来创建抽象接口。您也许可以使用 pimpl 模式为您希望客户端限制使用的每种类型创建代理接口?

例如。

class ExportedAbstractProxyObject

public:
    virtual void do_stuff() = 0;
;


#include <3rdPartyType.h>

class ProxyObject

public:
    void do_stuff()  pimpl_.actually_do_stuff(); 
private:
    3rdPartyType pimpl_;
;


class ExportedAbstractProxyOtherObject

public:
    virtual void do_stuff_with_thing(ExportedAbstractProxyObject* thing) = 0;
;


class ProxyOtherObject

public:
    void do_stuff_with_thing(ExportedAbstractProxyObject* thing)  thing->do_stuff(); 
;

这样您就可以愉快地导出您喜欢的任何接口,并在您的 DLL 中完全隐藏实现和第 3 方类型。缺点是您显然必须创建所有这些代理对象接口。

【讨论】:

我喜欢继承方法,但它并不总是你可以控制的。例如,当正在创建的 DLL 链接到第三方创建的静态函数库时。【参考方案2】:

您说您想阻止某些类的可见性,但您的示例仅隐藏了一个方法。

如果类不构成 DLL 的“公共”接口的一部分,则不需要发布标头。例如:

// foo.h
class Bar;

class Foo

    private Bar* _bar;
    ...

这里的“Bar”是实现的一部分,所以不需要发送它的标题。如果 Bar 仅由 Foo 使用,您也可以在 Foo 的私有/受保护范围内定义它。

【讨论】:

不幸的是,在我的情况下,我更愿意将条件编译函数公开。 那么我会推荐 Vusak 的方法。使客户端依赖于接口或抽象类是控制反转 (IoC) 的基础,并允许您编写易于进行单元测试的高度模块化的代码。【参考方案3】:

这类事情通常使用public/protected/private 声明,可能还有friends,而不是预处理器条件来完成。但是在我编写的一个程序中,将一组函数声明为私有或受保护(因为需要访问它们的独立函数的数量)更成问题,我在伪私有函数前面加上了前缀带有下划线的名称(以及解释原因的清晰 cmets),以使读者清楚这些函数并非用于一般用途。

【讨论】:

是的,但是使用下划线您仍然必须定义这些函数中使用的类型。因此必须向 DLL 的用户提供更多的头文件或前向声明。我真的只想提供 2 个头文件而不是 60 个。【参考方案4】:

不完全确定您要问什么,但如果成员函数打算对类私有,请使用“private:”关键字将它们设为私有。

如果它们打算被其他类在该类所在的模块的上下文中使用,但您不希望外部实体知道它们,请将它们公开,但从“接口”派生类基类,并将该接口基类公开给外部实体。

【讨论】:

嗯,是的,但不幸的是,'private' 关键字不会阻止应用程序在编译时链接到 DLL,因为它需要成员函数参数的完整定义。还使用“private”关键字限制了该成员函数在数据包中的使用方式。

以上是关于条件成员函数的主要内容,如果未能解决你的问题,请参考以下文章

java之类的封装

指向实例的成员函数而不是类的指针

编译器似乎没有找到类 llvm::Instruction 的成员函数

C++的=重载问题,怎样为两个有相同成员的类赋值

如何根据条件选择每个组的 x 或 y 成员

C++|详解类成员指针:数据成员指针和成员函数指针及应用场合