C++ 库制作,在 header 中没有朋友的情况下隐藏 destructor/constructor

C++ library making, hiding destructor/constructor without friend in header

我必须创建一个共享库,现在我面临以下问题。 (MSVC2015)。我想像 class A 那样隐藏 constructor/destructor,而我有一个像 B 那样的 "factory" class。

class A {
     public:
     private:
         A() {};
         ~A() {};
     friend class B;
};

class B {
   public:
   B() {};
   ~B() {};
    A * Create() { return new A(); };
};

所以我编译这两个 classes 来创建一个 .lib 和一个 .dll。我的问题是,如果我给用户 header 文件,其中包含 class A 的定义加上 .lib 和 .dll,但从这个 [=26= 中删除 friend class B 行] 那么它会是一个有效的解决方案吗?换句话说: "friend class" 是仅在编译时需要还是在运行时也需要?

感谢您的帮助!

如果你用朋友线编译它然后删除朋友线,你的用户总是可以再次添加它。您的用户也可以将函数从私有更改为 public.

如果 A::A() 的实现由您的库导出是一个不同的问题,并且在 windows 下由 declspec(dllexport/import) 属性确定,因此独立于 private/public 它可能是否对用户可用。据我目前所见,public,私有和友元声明对编译的 dll 没有影响,使用 member-pointers 你甚至可以在不编译 dll 的情况下访问私有成员。但是在这一点上不能保证。

真正的问题是:你为什么要这样做?
如果您在 header 中声明此构造函数是私有的并且只能由 B 访问,请在 B 中提供一个工厂函数并(希望)在您的文档中声明用户应该使用此工厂, 那么你已经做了一切合理的事情来阻止用户使用这个 class。如果他还是这样,那就不是你的错了。

编辑:(接受挑战)

我刚刚用 MSVC 2015 编译了以下 .dll:

//class.cpp
#include "class.h"

void bar(C & c)
{
    c.privatePrint();
}

//class.h
#pragma once
#include <string>
#include <iostream>

class C {
private:
    std::string str;
    C() :str("foo") {}

    void print() {
        std::cout << str << std::endl;
    }

    void privatePrint() {
        std::cout << "private " << str << std::endl;
    }

    friend _declspec(dllexport) void bar(C& c);
};

_declspec(dllexport) void bar(C& c);

并将其加载到以下 .exe 中:

//main.cpp
#include <iostream>
#include "class.h"
#pragma comment(lib,"../Debug/lib.lib")

int main(){
    C c;

    bar(c);
    c.print();
}

//class.h
class C {
public:
    std::string str;
    C() :str("foo") {}

    void print() {
        std::cout << str << std::endl;
    }
};

_declspec(dllimport) void bar(C& c);

输出:

private foo
foo

所以我们学到的是:这里的构造函数是在 class 定义中定义的,所以即使没有 _declspec(dllexport/import) 可执行文件也可以访问它。我们实际上 可以 将 private 更改为 public 并可以访问构造函数。 member-function print().
也是如此 即使我不仅省略了可执行文件 header bar() 的 fried 声明,而且省略了 bar() 使用的完整函数定义,但它不是bar 打印字符串的问题 - 仅仅是因为 bar() 的代码已经编译在 .dll.

很明显,class 完全由其 member-variables 及其布局定义,函数和访问说明符都不需要相同。 但是我没有声明这符合标准,保证有效或应该完成