首页 > 解决方案 > 静态变量在 .dll/.lib 中显示不同的行为

问题描述

我试图了解静态在 dll/lib 中的工作原理,所以我做了以下测试:

创建三个项目:

1) libadd              //has an class Object and a template class Single<T>
2) libuseadd           //has a function useadd which will call 
                       // Single<Object>::instance in libadd
3) app                 // main() will call 
                       // Single<Object>::instance in libadd and
                       // useadd in libuseadd

//===========libadd========================

//add.h
#pragma once

#include <iostream>

class __declspec(dllexport) Object
{
    public:
        Object():cnt_(0){}
        void speak()
        {
            std::cout<<"object:"<<cnt_++<<std::endl;
        }
    private:
        int cnt_;
        
};

template<typename T>
class __declspec(dllexport) Single
{
public:
    static T& instance()
    {
        static T t;
        return t;
    }

};

//add.cpp
#include "add.h"

//CMakelists.txt

cmake_minimum_required(VERSION 3.9 FATAL_ERROR)                                                                                                                                                                                                                           
set(PORJ_NAME libadd)
project(${PORJ_NAME})

add_library(${PORJ_NAME}
    add.h
    add.cpp
)

//===========libuseadd========================

//useadd.h
#pragma once

__declspec(dllexport) void useadd();

//useadd.cpp
#include "useadd.h"
#include "add.h"

void useadd()
{
    Object& obj = Single<Object>::instance();
    obj.speak();
    
}

//CMakeLists.txt

cmake_minimum_required(VERSION 3.9 FATAL_ERROR)                                                                                                                                                                                                                           
set(PORJ_NAME libuseadd)
project(${PORJ_NAME})

add_library(${PORJ_NAME}
    useadd.h
    useadd.cpp
)

target_include_directories( ${PORJ_NAME}
    PRIVATE 
        add/
    PUBLIC
)

target_link_libraries (${PORJ_NAME}
    PRIVATE
        bin/libadd.lib
)

//=====================main.cpp=====================

#include "add.h"
#include "useadd.h"

int main(int argc, char* argv[])
{
    Object& obj1 = Single<Object>::instance();
    obj1.speak();
    
    useadd();

    return 0;
}

//CMakelists.txt

cmake_minimum_required(VERSION 3.9 FATAL_ERROR)                                                                                                                                                                                                                           
set(PORJ_NAME app)
project(${PORJ_NAME})

add_executable(${PORJ_NAME}
    main.cpp
)

target_include_directories( ${PORJ_NAME}
    PRIVATE 
        add
        useadd
    PUBLIC
)

target_link_libraries (${PORJ_NAME}
    PRIVATE
        libadd.lib
        libuseadd.lib
)

如果结果是:

对象:0

对象:1

那么这意味着 instance() 函数中的静态变量只有一个实例(正确)。

如果结果是:

对象:0

对象:0

那么这意味着 instance() 函数中的静态变量是重复的(错误!)

我发现结果取决于库是编译为 dll 还是 lib。当libadd 和libuseadd 都编译为dll 时,甚至取决于实例函数在.h 或cpp 中的实现。假设我们必须将 add.h/add.cpp 更改为:

//when libadd/libuseadd all are dll, we have to use the codes below to 
//make it work correctly.
template<typename T>
class __declspec(dllexport) Single
{
public:
    static T& instance();
};
template  class   Single<Object>;

//add.cpp
#include "add.h"

#include "add.h"

template<typename T>
T& Single<T>::instance()
{
    static T t;
    return t;
}

在此处输入图像描述

我想知道为什么

更新(删除模板和类对象):

//===========libadd========================

//add.h
#pragma once

#include <iostream>


class __declspec(dllexport) Single
{
public:
    static int& instance()
    {
        static int t = 0;
        int tmp = t++;
        return tmp;
    }

};

//add.cpp
#include "add.h"

//===========libuseadd========================

//useadd.h
#pragma once

__declspec(dllexport) void useadd();

//useadd.cpp
#include "useadd.h"
#include "add.h"

#include <iostream>
using namespace std;
void useadd()
{
    cout<< Single::instance() <<endl;
}

//===========main.cpp=======================

//main.cpp
#include "add.h"
#include "useadd.h"

using namespace std;
int main(int argc, char* argv[])
{
    cout << Single::instance() << endl;
    useadd();
    return 0;
}

在此处输入图像描述

标签: c++visual-c++dll

解决方案


推荐阅读