C++库制作,隐藏在标头中没有友元的析构函数/构造函数

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

本文关键字:友元 析构函数 构造函数 隐藏 C++      更新时间:2023-10-16

我必须创建一个共享库,现在我面临以下问题。(MSVC2015)。我想像 A 类一样隐藏构造函数/析构函数,并且我有一个像 B 这样的"工厂"类。

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

因此,我编译这两个类以创建 .lib 和.dll。我的问题是,如果我给用户包含类 A 的定义以及 .lib 和 .dll 的头文件,但从此标头中删除行友元类 B,那么它会是一个有效的解决方案吗?换句话说:"友元类"是仅在编译时需要,还是在运行时也需要?

感谢您的帮助!

如果您使用好友行编译它,然后删除好友行,则用户始终可以再次添加它。您的用户还可以将函数从专用更改为公共。

如果库导出A::A()的实现是一个不同的问题,并且在由 declspec(dllexport/import) 属性确定的窗口下,因此独立于私有/公共,它可能可供用户使用,也可能不可用。从我目前所看到的情况来看,公共、私有和友元声明对编译的 dll 没有影响,使用成员指针,您甚至可以在不编译 dll 的情况下访问私有成员。但是在这一点上没有保证。

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

编辑:(接受挑战)

我刚刚用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

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

因此,很明显,一个类完全由其成员变量及其布局定义,函数和访问说明符都不需要相同。但是,我并没有说明这符合标准,保证有效或应该完全完成