Статический объект, на который нет ссылок, не будет создан в случае статической библиотеки? - PullRequest
0 голосов
/ 12 июля 2011

Среда разработки GNU GCC (g ++) 4.1.2, CMAKE

После того, как я прочитал какую-то авторитетную книгу Джеймса Коплина «Расширенные стили программирования и идиомы C ++» (должен признать, что эта книга довольно старая. Поэтому некоторые из вас могут счесть ее устаревшей.), Я обнаружил, что «Образец идиомы» был весьма полезен для ослабления взаимозависимости между иерархией классов наследования. Поэтому попытался создать простой проект для обработки этой идиомы следующим образом.

Спасибо за ваше терпение заранее, пока я перечисляю весь исходный код следующим образом.

В CMakeLists.txt

cmake_minimum_required(VERSION 2.6)

project(TestProject)

add_library(exemplar_idiom STATIC base.cpp derived1.cpp)

add_executable(exemplar_example main.cpp)
target_link_libraries(exemplar_example exemplar_idiom)
set_target_properties(exemplar_example PROPERTIES OUTPUT_NAME exemplar_example)

In instancelar.h

#ifndef EXEMPLAR_H_
#define EXEMPLAR_H_

class   Exemplar
{
public:
    Exemplar() {};
};

#else
#endif  //  EXEMPLAR_H_

В базе.ч

#ifndef BASE_H_
#define BASE_H_

#include <string>

class   Exemplar;

class   Base
{
public:
    Base(Exemplar /* no use */);
    Base();

    virtual Base*   make(const std::string& key);

    virtual void    memberMethod1();
    virtual int     memberMethod2();

    virtual ~Base() {};
protected:
    static  Base*   list_;
    Base*   next_;

};

#else
#endif  //  BASE_H_

В base.cpp

#include <string>
#include <iostream>

#include "exemplar.h"
#include "base.h"

Base* Base::list_ = 0;
static Exemplar exemplar;
static Base baseInstance(exemplar);
Base*   baseExemplar = &baseInstance;

Base::Base(Exemplar /* no use */) : next_(list_)
{
    std::cout << "Base object exemplar ctor" << std::endl;
    list_ = this;
}

Base::Base() : next_(0) {}

Base* Base::make(const std::string& key)
{
    Base* retval = 0;

    for (Base* a = list_; a; a = a->next_)
    {
        if (a != baseExemplar)
        {
            if ((retval = a->make(key)))
            {
                break;
            }
        }
        else if (key == "base")
        {
            retval = new Base();
            break;
        }
    }
    return retval;
}

void Base::memberMethod1()
{
    std::cout << "base class memberMethod1() called." << std::endl;
}

int Base::memberMethod2()
{
    std::cout << "base class memberMethod2() called." << std::endl;
    return 0;
}

В производном 1.h

#include "base.h"

#ifndef DERIVED1_H_
#define DERIVED1_H_

class   Exemplar;

class   Derived1 : public Base
{
public:
    Derived1(Exemplar /* no use */);

    //  Conventional default constructor which will be used to instantiate normal object
    Derived1();

    virtual Derived1*   make(const std::string& key);
    virtual void    memberMethod1();
    virtual int     memberMethod2();
};

#else
#endif  //  DERIVED1_H_

В производном .cpp

#include <iostream>

#include "exemplar.h"
#include "derived1.h"

static Exemplar exemplar;
static Derived1 derived1Exemplar(exemplar);

Derived1::Derived1(Exemplar a) : Base(a)
{
    std::cout << "Derived object exemplar ctor" << std::endl;
}

Derived1::Derived1() : Base() {}

Derived1*   Derived1::make(const std::string& key)
{
    Derived1* retval = 0;
    if (key == "derived1")
    {
        retval = new Derived1();
    }
    return retval;
}

void    Derived1::memberMethod1()
{
    std::cout << "Derived1::memberMethod1" << std::endl;
}

int    Derived1::memberMethod2()
{
    /*  dummy   */
    std::cout << "Derived1::memberMethod2" << std::endl;
    return 0;
}

В main.cpp

#include "base.h"

extern Base* baseExemplar;

int main()
{
    Base *ptr = baseExemplar->make("derived1");
    if (ptr) {
        ptr->memberMethod1();
        ptr->memberMethod2();
    }

    Base *ptr2 = baseExemplar->make("base");
    if (ptr2) {
        ptr2->memberMethod1();
        ptr2->memberMethod2();
    }

    return 0;
}

Когда мы слегка изменим файл CMakeLists.txt, чтобы создать общий объект, заменив ключевое слово «STATIC» на «SHARED», я смог увидеть ожидаемое поведение. Я мог видеть, что в результате создания статического объекта объекта класса Derived1 (с аргументом Exemplar) цепочка списка ссылок в базовом классе поддерживается должным образом. И после этого, при вызове виртуального метода make () из базового экземпляра объекта в main (), я мог видеть, что и объект Derived1, и базовый объект были успешно созданы с помощью указания ключевой строки «output1» и «base».

Однако, если я верну ключевое слово «SHARED» обратно к «STATIC», я смогу увидеть другой результат. Похоже, что такой статический объект (образец объекта Derived1) вообще не создается. Поэтому он не смог создать объект Derived1, вызвав метод-член make ("производный1").

Есть ли способ избежать этой проблемы, даже в случае статической библиотеки? Я хотел бы ожидать побочный эффект конструктора статических объектов в отношении списка ссылок Базового класса.

Я не совсем уверен, имеет ли смысл то, что я пытаюсь сделать здесь. Тем не менее, было бы очень признательно, если бы вы могли дать мне указатель на выше.

1 Ответ

1 голос
/ 12 июля 2011

Да, компоновщик GNU ld добавляет только те символы, которые, по его мнению, действительно используются. ld может только проверять, используется ли символ, но не использовать побочные эффекты инициализации символа.

Таким образом, результатом будет ситуация, подобная вашей, сбои из-за неинициализации неиспользуемого статического объекта.

Вы можете использовать --whole-archive при компоновке, чтобы указать компоновщику включить все символы. Недостатком является то, что символы, которые вы никогда не захотите использовать, также будут добавлены, что приведет к увеличению размера кода.

Также следует отметить следующее:

Компоновщик удаляет неиспользуемые объектные файлы, только если они из библиотеки. Объектные файлы, явно переданные компоновщику, всегда будут связаны.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...