static и extern
От: Аноним  
Дата: 22.10.07 18:13
Оценка: :)
Всем привет!

Страуструпа и Шилда про сабж перечитал уже раз на 10, всё равно ничего не понятно...
Объясните плиз про static и extern на пальцах. Ну как ни напишу, компилятор всегда пишет, то "error C2159: more than one storage class specified", то реализацию не находит.

А конкретно:

A.h

namespace A {
extern static int a; // это означает что переменная объявлена но инициализоваться будет потом
}

A.cpp
static A::a = 0;

B.cpp
include "A.h"

A::a = 10;

Компилятор всё время не доволен!

Разъясните плиз!

Спасибо.
Re: Может ты про extern и const?
От: Erop Россия  
Дата: 22.10.07 21:03
Оценка:
Здравствуйте, Аноним, Вы писали:

А>//A.h 

А>namespace A {
А>  extern static int a; // это означает что переменная объявлена но инициализоваться будет потом
А>}

А>//A.cpp
А>static A::a = 0;

А>//B.cpp
А>include "A.h"

А>A::a = 10;

А>Компилятор всё время не доволен!

А чего ты хочешь добиться?
Чтобы в кажой единице трансляции переменная A::a была одна и та же или чтобы они были разными?
Если одна и та же, то логично сделать так:
//A.h
namespace A {
    extern int a;
}//namespace A

//A.cpp
#include "A.h"
int A::a = 0;

// B.cpp
#include "A.h"
void foo( int b )
{
    A::a = b;
}


А если хочешь, чтобы была разной, то не понятно зачем объявлять в хедере предварительно. Ведь тогда в каждой единице трансяции прийдётся определять... Но это тоже возможно, конечно, только ты сначала скажи, точно ли это то, что тебе нужно...
А>Разъясните плиз!

А>Спасибо.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: static и extern
От: Warturtle  
Дата: 24.10.07 11:20
Оценка: :)
Здравствуйте, Аноним, Вы писали:

А>Всем привет!


А>Страуструпа и Шилда про сабж перечитал уже раз на 10, всё равно ничего не понятно...

А>Объясните плиз про static и extern на пальцах. Ну как ни напишу, компилятор всегда пишет, то "error C2159: more than one storage class specified", то реализацию не находит.

А>А конкретно:


namespace A {
  extern static int a; // это ж какой-то Старый Новый Год получается!
}
Re[2]: static и extern
От: anidal  
Дата: 25.10.07 12:46
Оценка: -1
W>Здравствуйте, Аноним, Вы писали:

W>
namespace A {
W>  extern static int a; 
W>}
W>


В нек-х компиляторах для указания что переменная не будет инициализирована используют конструкции типа __no_init int a;
В вашем случае надо в том месте, где переменная будет томом инициализироваться написать:


static int a;



void Init_a(void){
a=5;
}

А в месте, где ее будут использовать —

extern int a;

И компилятор успокоится.
Re: static и extern
От: LaptevVV Россия  
Дата: 25.10.07 13:07
Оценка: 2 (1)
Здравствуйте, Аноним, Вы писали:

А>Страуструпа и Шилда про сабж перечитал уже раз на 10, всё равно ничего не понятно...

Читай сюда

Межмодульное взаимодействие
Как указано в стандарте (см. п.п. 3.2/1 в [1]), единица трансляции не должна содержать более одного определения любой переменной, функции, класса, перечисления и шаблона (в каждой области видимости, естественно). Это — так называемое правило одного определения (One-Definition Rule, ODR). В первую очередь это правило касается определений классов, переменных и функций. Для переменной компилятор по ее определению вычисляет размер и резервирует место в памяти, поэтому при наличии более одного определения в одной области видимости у него «возникают проблемы». Для класса компилятор тоже вычисляет размер, хотя обычно и не резервирует место в памяти — это делается при объявлении объектов класса.
ПРИМЕЧАНИЕ
Для статических полей класса память резервируется до первого объявления объекта класса — при определении статического поля.


Для функции компилятор генерирует код, который, в конечном счете, тоже займет свое место в памяти.
А вот объявлений может быть несколько. Объявление лишь добавляет некоторое имя в данную область видимости и обычно используется для согласования типов. Однако при разделении программы на отдельные модули возникают вопросы о взаимодействии определений и объявлений, прописанных в разных модулях. Естественно, речь не идет о локальных объектах — с ними все ясно.
Межмодульные переменные и функции
Начнем с простых переменных. Допустим, у нас есть два модуля A.cpp и B.cpp. В модуле A определена целая переменная i вне всех классов и функций:

int i = 2;

Такая переменная называется глобальной. В файле A она видна от точки определения и до конца файла. Однако в модуле B эта переменная не видна. И если вдруг нам потребуется в модуле B присвоить ей другое значение, у нас возникнут некоторые проблемы. Нельзя просто написать:
i = 1;

В этом случае компилятор при обработке модуля B «не видит» модуль A и ничего не знает об определенной там переменной, поэтому мы получим сообщение о неопределенной переменной. Также нельзя написать:
int i = 1;

Такая запись является повторным определением. Компилятор-то «возражать» не станет — он транслирует модули по отдельности, а вот компоновщик будет «воротить нос» и сообщит, что одна и та же переменная определена дважды. Для таких случаев в С++ включено специальное ключевое слово extern. В модуле B надо объявить переменную следующим образом:
extern int i;

После этого можно использовать переменную i в файле B любым разрешенным способом. Например, присвоить новое значение:
i = 1;

Однако попытка совместить объявление с присвоением значения является ошибкой:
extern int i = 1;

Такая запись служит определением, поэтому мы опять получим от компоновщика сообщение о повторном определении.
ПРИМЕЧАНИЕ
Хотя ключевое слово extern в стандарте определено как один из четырех классов хранения, проще понимать его как обозначение «внешнего» имени для данного модуля. Имя называется внешним по отношению к модулю, если объект с этим именем не определен в данном модуле.


Аналогичная картина наблюдается и с функциями. Определение функции включает тело, а объявлением является прототип. Пусть в модуле A определена функция:
void f(void) 
{ cout << "f()" << endl; }

Для того чтобы эту функцию можно было использовать в модуле B, нужно объявить там ее прототип:
void f(void);

Слова extern писать не требуется, хотя и не запрещается. Следующие прототипы эквивалентны:
void f(void);
extern void f(void);

Локализация имен в модуле
Итак, определив глобальную переменную или функцию в некоторой единице трансляции, мы должны придерживаться определенных правил, чтобы не возникало конфликтов имен. Говорят, что для имен глобальных переменных и функций применяется внешняя компоновка (extenal linkage), то есть эти имена становятся видны компоновщику во время компоновки программы.
Однако иногда бывает нужно, чтобы глобальная переменная или функция были видны только в том файле, где определены. Это позволяет сделать атрибут static, например:
static int a = 1;
static void f(void) { ... }

Для определенных таким образом имен применяется внутренняя компоновка (internal linkage) — они являются локальными в модуле, где определены. Таким образом, можно говорить, что глобальные имена обладают свойством внешней или внутренней компоновки.
Разберемся с некоторыми подробностями на примере функций. Пусть у нас есть, как обычно, два модуля A.cpp и B.cpp:
//--модуль A.cpp
void f1(void) {...};            // определение глобальной функции
static void f2(void){...};      // определение локальной функции
//--модуль B.cpp
f1();                           // вызов глобальной функции
f2();                           // ОШИБКА!! -- вызов невидимой функции

Функция f2() не видна в модуле B, поэтому ее вызов ведет к ошибке трансляции.
Имя функции с атрибутом static должно быть уникальным в данном модуле, но может повторяться в других модулях. Более того, локальная в модуле функция (с атрибутом static) перекрывает глобальную функцию в пределах модуля (аналогично тому, как локальная переменная в теле функции перекрывает глобальную). Например, в следующем примере функция f1() в модуле B.cpp перекрывает глобальную функцию, определенную в модуле A.cpp.
//--модуль A.cpp
void f1(void) {...};            // определение глобальной функции
static void f2(void){...};      // определение локальной функции
f1();                           // вызов глобальной функции
//--модуль B.cpp
static void f1(void) {...};     // определение локальной функции
f1();                           // вызов локальной функции

Все то же самое относится и к глобальным переменным с атрибутом static.
Атрибут static в данном случае похож на модификатор доступа private, работающий на уровне файла. Налицо два совершенно разных смысла одного ключевого слова: с одной стороны, для локальных переменных static означает класс хранения (в статической памяти); с другой стороны, на уровне модуля атрибут static имеет смысл ограничителя видимости имен. Последнее объявлено устаревшим, поэтому применение атрибута static в таком значении является нежелательным (см. п. D2 в [1]). Вместо этого для решения проблем локализации в С++ включили пространства имен, которые мы рассмотрим далее.
Константы по умолчанию компонуются внутренним образом. Это означает, что при объявлении константы нет необходимости указывать атрибут static, чтобы сделать ее локальной в модуле. Поэтому в разных модулях можно объявлять глобальные константы с одинаковыми именами — конфликта при компоновке не происходит. Более того, чтобы сделать константу, объявленную в одном модуле, видимой в другом, нужно использовать слово extern:
// модуль A.cpp
extern const int a = 2;
// модуль B.cpp
extern const int a;

Тогда компоновщик будет считать, что в модулях A.cpp и B.cpp используется одна и та же целая константа с именем a. Если мы пропустим слово extern в объявлении константы в модуле А.cpp, то получим локализованную константу, и при обработке объявления в модуле В.cpp компоновщик выдаст сообщение о неопределенном имени.
Внутренняя компоновка констант оказывает «медвежью услугу» в шаблонах. Поскольку строковые литералы — это объекты со свойством внутренней компоновки, использовать их в качестве аргумента шаблона не разрешается:
template<char const *str>
class Template { ... };
Template<"literal"> T;            // Ошибка! 
Нельзя использовать и глобальный указатель:
template<char const *str>
class Template { ... };
char const *s = "Literal";
Template<s> T;                    // Ошибка!

Однако глобальный символьный массив использовать можно:
template<char const *str>
class Template { ... };
char const s[] = "Literal";
Template<s> T;                    // Ошибки нет!

Для функций, объявленных как подставляемые (с ключевым словом inline), по умолчанию тоже применяется внутренняя компоновка. Функция, определенная в одном модуле, не видна в другом модуле. Даже если определения в модулях абсолютно синтаксически совпадают — это все-таки могут быть разные функции, например:
// модуль A.cpp
const int a = 7;
inline void f(void) 
{ cout << a << "f()\n"; }
// модуль B.cpp
const int a = 10;
inline void f(void) 
{ cout << a << "f()\n"; }

При вызове первой функции в модуле А получим на экране 7f(), а при вызове второй в модуле В на экране появится 10f().
Так же как и определение класса, определение подставляемой функции может быть включено в программу несколько раз — по одному определению в каждом модуле. Чтобы гарантированно иметь в разных модулях одно и то же определение, мы вынесем его в отдельный модуль и подключим этот модуль в нужных файлах с помощью директивы #include — компоновщик против не будет. Естественно, определение не должно зависеть от локализованных имен, как в приведенном ранее примере.
Можно сделать подставляемую функцию глобальной — точно так же, как и константу, указав в определении ключевое слово extern:
// модуль A.cpp
// Определение глобальной inline-функции
extern inline void f(void) 
{ cout << a << "f()\n"; }

В другом модуле достаточно указать прототип:
// модуль B.cpp
// объявление внешней inline-функции
void f(void);

К прототипу можно добавить спецификаторы extern и inline:
extern inline void f(void);

Как видите, в этом случае можно обойтись без подключения модуля с определением.

[1] — это стандарт С++
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[3]: static и extern
От: Аноним  
Дата: 25.10.07 13:08
Оценка:
Здравствуйте, anidal, Вы писали:


W>>Здравствуйте, Аноним, Вы писали:


W>>
namespace A {
W>>  extern static int a; 
W>>}
W>>


A>В нек-х компиляторах для указания что переменная не будет инициализирована используют конструкции типа __no_init int a;

A>В вашем случае надо в том месте, где переменная будет томом инициализироваться написать:


A>
A>static int a;



A>void Init_a(void){
A>a=5;
A>}
A>

A>А в месте, где ее будут использовать —

A>
A>extern int a;
A>

A>И компилятор успокоится.

А почему так нельзя?
static int a = 5;
Re[2]: static и extern
От: Erop Россия  
Дата: 25.10.07 13:35
Оценка:
Здравствуйте, LaptevVV, Вы писали:

LVV>Читай сюда

LVV>

...

LVV>[1] — это стандарт С++

А ссылочку на прцитированный документ не подскажите? Я может сотрудникам бы порекомендовал в него заглядывать...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: static и extern
От: LaptevVV Россия  
Дата: 25.10.07 14:01
Оценка: 10 (1)
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, LaptevVV, Вы писали:


LVV>>Читай сюда

LVV>>

...

LVV>>[1] — это стандарт С++

E>А ссылочку на прцитированный документ не подскажите? Я может сотрудникам бы порекомендовал в него заглядывать...

В моей книжке "С++.Объектно-ориентированное программирование", которую издательство Питер только что выпустило.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.