【问题标题】:Why does linker fail when accessing static member in inline constructor为什么在访问内联构造函数中的静态成员时链接器会失败
【发布时间】:2014-11-27 09:51:37
【问题描述】:

刚开始为一个非常简单的内部使用自动化测试框架编写代码。 (我知道那里有数百个 - 也有非常好的,但目前这并不有趣,所以请不要指出其中任何一个;))然后我遇到了以下问题,我可以'不解释,所以请你帮忙。

我有以下代码作为 DLL 的一部分:

(代码还只是一个雏形,我花了不到 2 分钟的时间来写,所以它的逻辑、结构 - 没有任何东西 - 以任何方式改进。)

h 文件:

#pragma once

#ifdef __DLL__   // Defined in DLL-project
    #define DLLEXPORT __declspec( dllexport )
#else
    #define DLLEXPORT
#endif

class DLLEXPORT AutoTest
{
public:
            enum    eTestID {TESTID_SomeFunction};
                    AutoTest(eTestID id, LPVOID lpData)
                    {
                        if(sm_bTestsActive)
                            ExecTest(id, lpData);
                    }
            void    ActivateTests();

private:
    static  void    ExecTest(eTestID id, LPVOID lpData)
                    {
                    }
    static  BOOL    sm_bTestsActive;
};

cpp 文件:

#include "StdAfx.h"
#include "AutoTest.hpp"

BOOL AutoTest::sm_bTestsActive = FALSE;

void AutoTest::ActivateTests()
{
    sm_bTestsActive=TRUE;
}

编译得很好,并且生成了 DLL。

但这是我的问题 - 使用以下方法实例化类时:

AutoTest(AutoTest::TESTID_SomeFunction, &SomeData);

在主应用程序中,链接器失败

error LNK2001: unresolved external symbol "private: static int AutoTest::sm_bTestsActive" (?sm_bTestsActive@AutoTest@@0HA)

这很有趣 - 如果我将构造函数移动到 cpp 文件(未内联)它就可以正常工作!?!?

代码如下:

h 文件:

#pragma once

#ifdef __DLL__   // Defined in DLL-project
    #define DLLEXPORT __declspec( dllexport )
#else
    #define DLLEXPORT
#endif

class DLLEXPORT AutoTest
{
public:
            enum    eTestID {FK3059};
                    AutoTest(eTestID id, LPVOID lpData);
            void    ActivateTests();

private:
    static  void    ExecTest(eTestID id, LPVOID lpData)
                    {
                    }
    static  BOOL    sm_bTestsActive;
};

cpp 文件:

#include "StdAfx.h"
#include "AutoTest.hpp"

BOOL AutoTest::sm_bTestsActive = FALSE;

AutoTest::AutoTest(eTestID id, LPVOID lpData)
{
    if(sm_bTestsActive)
        ExecTest(id, lpData);
}

void AutoTest::ActivateTests()
{
    sm_bTestsActive=TRUE;
}

(粘贴后我对代码进行了一些小修改,因此可能存在也可能不存在简单的语法错误。)

另外,如果我从内联版本构造函数中删除对静态成员的引用,它工作正常。

关于为什么内联版本不起作用的任何想法?

【问题讨论】:

  • 我猜你是从另一个库调用构造函数并且没有与 DLL 链接。
  • @DDrmmr 文件/类是我们的“主干”DLL 的一部分,从“客户端”大量使用。而且它也不适用于非内联版本。
  • DLLEXPORT 是如何定义的?
  • 该 DLL 的用户不应该有一个标记为 DLL 导入的类声明吗?您不会从应用程序中导出类。无论如何,这绝对是高度特定于编译器/操作系统的。
  • @hvd 这通常是通过巧妙地定义 DLLEXPORT 宏和链接器标志来完成的,但不清楚这里是如何完成的。

标签: c++ windows dll static linker


【解决方案1】:

注意你对DLLEXPORT的定义。

确保在构建 DLL 时正确扩展为 __declspec(dllexport),或在构建客户端时正确扩展为 __declspec(dllimport)

我建议使用名称比通用 DLLEXPORT 更具体的宏(以避免与其他同名宏发生冲突)。

从内联成员函数访问静态数据成员对我来说很好(用 VS2013 测试)。

最少的复制:

使用空的 DLL 项目和空的控制台应用程序项目创建 Visual Studio 解决方案。

在DLL项目里面添加两个文件:

DllClass.h

#pragma once

#ifndef TEST_DLL_CLASS
#define TEST_DLL_CLASS __declspec(dllimport)
#endif

class TEST_DLL_CLASS DllClass
{
public:
    DllClass();

    int GetMember() const
    {
        return m_data1;
    }

    static int GetStaticMember()
    {
        return sm_data2;
    }

private:
    int m_data1;
    static int sm_data2;
};

DllClass.cpp

#define TEST_DLL_CLASS __declspec(dllexport)
#include "DllClass.h"

int DllClass::sm_data2 = 2;

DllClass::DllClass()
    : m_data1(1)
{
}

在控制台应用项目中,添加一个文件:

Test.cpp

#include "..\DllTestClass\DllClass.h"
#include <iostream>
using namespace std;

#pragma comment(lib, "DllTestClass")

int main()
{
    DllClass dllClass;

    cout << dllClass.GetMember() << endl;
    cout << DllClass::GetStaticMember() << endl;
}

确保在构建控制台测试应用程序时,链接器可以找到 DLL .lib (DllTestClass.lib) 文件。
为此,您可以导航控制台应用程序的项目属性,转到:

Project Properties | Linker | Additional Library Directories

并将$(OutDir) 添加到其他库目录中,使其成为:

$(OutDir);%(AdditionalLibraryDirectories)

为我构建并正常工作。

【讨论】:

  • BartoszKP 击败了你,但对努力投了赞成票事实上你让我想到了这个名字......决定DLL_VISIBILITY 没有更好的单词。还有其他建议吗? (Discussion here)
  • @ClasG:我不知道你说的 “打败你” 是什么意思。老实说,@BartoszKP 的 initial 答案是错误的:我有一条评论指出了这一点,他诚实地同意了这一点,并删除了他原来的答案。 (然后,我删除了我的评论,因为错误的初始答案已被删除。)无论如何,对于命名,我会建议一些特定于您的 DLL 的名称,并精心制作以使其难以与其他名称发生冲突,例如 YOUR_COMPANY_NAME_YOUR_DLL_PROJECT_NAME_DLL_EXPORT (例如FOO_AUTO_TEST_DLL_EXPORT)。
  • 很抱歉没有将整个评论历史记录在我的脑海中:P 然而,对于我们的解决方案,这个名称应该更(半)通用。谢谢。
  • 没问题 :) 当然,随意选择您喜欢的名称。
【解决方案2】:

应该是:

#ifdef __DLL__   // Defined in DLL-project
    #define DLLEXPORT __declspec( dllexport )
#else
    #define DLLEXPORT __declspec( dllimport )
#endif

您可以使用dllimportdllexport 属性声明C++ 类。这些形式意味着整个类被导入或导出。以这种方式导出的类称为可导出类。

the documentation 中的更多信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-15
    • 2020-12-22
    • 2011-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多