путаница при статической инициализации - PullRequest
3 голосов
/ 14 июня 2010

Я очень запутался в некоторых понятиях в c ++.Например: у меня есть следующие два файла

//file1.cpp
class test
{
    static int s;
    public:
    test(){s++;}
};

static test t;
int test::s=5;

//file2.cpp
#include<iostream>
using namespace std;
class test
{
    static int s;
    public:
    test(){s++;}
    static int get()
    {
    return s;
    }
};

static test t;

int main()
{
    cout<<test::get()<<endl;
}

Теперь у меня вопрос:
1. Как два файла успешно связываются, даже если у них разные определения классов?
2. Являются ли статические члены sиз двух классов, связанных, потому что я получаю вывод как 7.

Пожалуйста, объясните эту концепцию статики.

Ответы [ 5 ]

3 голосов
/ 14 июня 2010

Они ссылаются, потому что компоновщик почти ничего не знает о языке C ++. Однако, если вы сделаете это, вы нарушите правило единого определения, и поведение вашей программы будет неопределенным. Написание неверного кода не является хорошим способом изучения C ++. Кроме того, у вас, похоже, возникает много вопросов о статических переменных - концепция на самом деле не такая уж и сложная - какой учебник по С ++ вы используете, который плохо объясняет это?

1 голос
/ 14 июня 2010

Static - странный зверь в C ++, который имеет разные значения в зависимости от контекста.

В File1.cpp «static int s» в определении класса указывает, что s является статическим членом, то есть, является общим для всех экземпляров теста.

«Статический тест t», однако, имеет другое значение: статическая глобальная переменная существует только в модуле компиляции и не будет видна другим модулям.Это поможет избежать путаницы с компоновщиком, если вы использовали одно и то же имя для двух разных вещей.В современном C ++ для этого можно использовать анонимные пространства имен:

namespace
{
  test t;
}

Это означает, что t внутри File1.cpp и t внутри File2.cpp являются отдельными объектами.

В File2.cpp,Вы также определили статический метод get: статический метод - это метод, который принадлежит классу, а не экземпляру, и который может обращаться только к статическим членам класса.

Последнее использование статического, которое вы сделалине использовать в вашем примере, это локальная статическая переменная.Локальная статическая переменная инициализируется при первом запуске и сохраняет свое значение на протяжении всего выполнения, немного как глобальная переменная с локальной областью действия:

int add()
{
  static value = 0;
  value++;
  return value;
}

Повторный вызов add () будет возвращать 1,затем 2, затем 3 ... Эта локальная статическая конструкция может быть полезна, например, для локальных кэшей.

1 голос
/ 14 июня 2010

Классы (для компоновщика) идентичны. get () - это просто встроенная функция, которую компоновщик никогда не видит.

static в классе не ограничивает члена областью файла, но делает его глобальным для всех экземпляров класса.

[edit:] Вы получите ошибку компоновщика, если вы добавите int test::s=5; также во второй файл.

0 голосов
/ 14 июня 2010

Unnice побочный эффект от того, как на самом деле работает C ++.Используйте пространства имен, чтобы отличать имена классов от внешних.

В прошлом я использовал довольно обременительное на практике правило: каждая библиотека и файл должны иметь личное пространство имен, чтобы избежать такой ссылкиконфликты.Или поместите вспомогательные классы прямо в определение основного класса.В любом случае: не загрязнять глобальное пространство имен и, самое главное, обеспечивать уникальность имен во всем проекте.(Пишу, что теперь я замечаю, что в значительной степени дословно использовал эту концепцию из Java.)

Мой поиск эквивалента статики C в C ++ был бесполезным ...

0 голосов
/ 14 июня 2010
  1. Как два файла успешно связываются, даже если у них разные определения классов?

Вы нарушили правило One Definition (ODR), по-разному определяя один и тот же класс в разных единицах перевода. Это вызывает ужасное неопределенное поведение . Это означает, что код позволяет делать все что угодно, в том числе, помимо прочего, делать то, что вы хотели, форматировать жесткий диск или вызывать неучтенное солнечное затмение.
Обратите внимание, что компиляторы / компоновщики не должны обнаруживать нарушения ODR.

  1. Связаны ли статические члены двух классов, потому что я получаю вывод как 7.

Может быть, а может и нет. Действительно, неопределенное поведение может сделать что угодно. То, что вы видите, является просто артефактом реализации вашего компилятора и компоновщика.
Вероятная реализация с радостью свяжет все ссылки на test::s с одним и тем же экземпляром, но позволит каждому модулю перевода иметь и ссылаться на собственный объект t. (Так как класс имеет только inline функции, компоновщик, скорее всего, никогда даже не увидит ничего из test за исключением test::s и этих t экземпляров.)

...