Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > [Линковка] Как обратиться к классу, определенному


Автор: zim22 17.7.2009, 14:44
К функции xxx мы обращаться можем из main.cpp. Хотя она и определена в другом файле.
Как создать объект класса First в файле main.cpp? (не использую #include и файлы заголовков)

Код

// first.cpp
class First {
public:
  void fcn() { }
};
void xxx() { }

Код

// main.cpp
int main() {
  void xxx();
  xxx();

  First f; // error
  f.fcn();
}


Автор: azesmcar 17.7.2009, 14:50
zim22

Указатель можно создать так
Код

class A;

int main()
{
    A* ptr;
}

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

Автор: mes 17.7.2009, 14:54
Цитата(zim22 @  17.7.2009,  13:44 Найти цитируемый пост)
Как создать объект класса First в файле main.cpp?

интересно, как для т.с. связано  "создание объекта"  и "линковка" ?

Автор: AlexT84 17.7.2009, 14:57
Не используя инклуда -- только заново описать класс First.

Автор: zim22 17.7.2009, 15:48
Цитата(mes @  17.7.2009,  14:54 Найти цитируемый пост)
интересно, как для т.с. связано  "создание объекта"  и "линковка" ?

я сейчас разбираюсь в том, как происходит сборка программмы из исходников. линковка - один из её этапов.
но до линковки не доходит, т.к. исходник даже не компилирууется. smile

класс определён в одном  translation unit
использовать его я хочу в другом  translation unit
значит необходимо обеспечить взаимодействие между юнитами.

http://gaubuali.wordpress.com/linkage-in-c-and-c/  сказано следующее:
Цитата

C and C++ provide for three levels of linkage:
• A name with no linkage denotes an entity that can’t be referenced via names from anywhere else.
• A name with internal linkage denotes an entity that can be referenced via names declared in the same scope or in other scopes of the same translation unit.
• A name with external linkage denotes an entity that can be referenced via names declared in the same scope or in other scopes of the same translation unit (just as with internal linkage), or   additionally in other translation units.


Цитата(azesmcar @  17.7.2009,  14:50 Найти цитируемый пост)
а вот создать обьект - никак без включения заголовочного файла

вы хотите сказать, что нельзя написать программу без использования заголовочных файлов?

Автор: azesmcar 17.7.2009, 15:51
Цитата(zim22 @  17.7.2009,  15:48 Найти цитируемый пост)
вы хотите сказать, что нельзя написать программу без использования заголовочных файлов?

почему это нельзя? Можно!
Код

int main() {}

вот она smile 

Цитата(zim22 @  17.7.2009,  15:48 Найти цитируемый пост)
класс определён в одном  translation unit
использовать его я хочу в другом  translation unit

Цитата(zim22 @  17.7.2009,  15:48 Найти цитируемый пост)
но до линковки не доходит, т.к. исходник даже не компилирууется

Как он будет компилироваться?
Код

class A;
int main()
{
    A* ptr = new A();
}

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

Автор: zim22 17.7.2009, 15:53
Цитата(azesmcar @  17.7.2009,  15:51 Найти цитируемый пост)
 Можно ли компилировать этот код, не зная есть ли класса A конструктор по умолчанию (например).

ну а почему с функциями прокатывает?

Автор: azesmcar 17.7.2009, 15:54
Цитата(zim22 @  17.7.2009,  15:53 Найти цитируемый пост)
ну а почему с функциями прокатывает? 

можно пример, чтобы было понятнее о чем идет речь?

Добавлено @ 15:57
подозреваю что речь идет об этом
Код

void foo();

int main()
{
    foo();
}


ну так с классами это тоже прокатывает. Тут указана сигнатура функции, компилятор знает все необходимое об этой функции и может оставить остальное линкеру. Если описать структуру класса - для классов будет тоже самое (в принципе это и происходит при включении заголовочного файла).

(непонятно зачем приписал extern, он тут не нужен, убрал).

Автор: zim22 17.7.2009, 15:57
Цитата(azesmcar @  17.7.2009,  15:54 Найти цитируемый пост)
можно пример, чтобы было понятнее о чем идет речь?

функция определена в другом файле. тем не менее линкер/компилятор её находит.
Код

// main.cpp
int main()
{
  void fcn();
  fcn();

    return 0;
}


Код

// file.cpp
void fcn() { }



Автор: azesmcar 17.7.2009, 16:02
zim22

Ну, прототип функции обявлен ведь.
Код

class A
{
public:
    A();
};

int main()
{
    A t;
}

с классами тоже можно. Вот так. А включение заголовочного файла только копирует его содержимое в текущий.

Автор: zim22 17.7.2009, 16:05
Цитата(azesmcar @  17.7.2009,  16:02 Найти цитируемый пост)
с классами тоже можно. Вот так.

у меня не получается. этап линковки не проходит.
Код

// main.cpp
class A {
public:
  A();
};

int main()
{  
   A t;
  return 0;
}


Код

// A.cpp
class A
{
public:
  A(){}
};

Автор: azesmcar 17.7.2009, 16:09
file1.cpp
Код

class A
{
public:
    A();
};

int main()
{
    A t;
}


file2.cpp
Код

#include <iostream>

//Тут тоже нужно
class A {
public:
    A();
};

A::A()
{
}

а вот эту часть
Цитата

class A {
public:
    A();
};

заменяют на #include а саму декларацию выделяют в заголовочный файл.

Автор: zim22 17.7.2009, 16:11
Цитата(azesmcar @  17.7.2009,  16:09 Найти цитируемый пост)
а вот эту часть заменяют на #include а саму декларацию выделяют в заголовочный файл.

большое спасибо! я ж говорил, что можно без #include обходиться smile

Автор: azesmcar 17.7.2009, 16:13
Цитата(zim22 @  17.7.2009,  16:11 Найти цитируемый пост)
что можно без #include обходиться 

А я как будто не соглашался smile 
#include по сути вставляет содержимое одного файла в другой, разумеется без него можно (если делать тоже самое самому) smile

Автор: zim22 17.7.2009, 16:15
Почему в случае определения функции внутри класса линкер не находит её?
Код

//A.cpp
class A
{
public:
  A();
  void fcn(){}
private:
  int data;
};

A::A() { }


Код

// main.cpp
class A {
public:
  A(); 
  void fcn();
};

int main()
{  
   A t;
   t.fcn();
  return 0;
}

Автор: azesmcar 17.7.2009, 16:19
zim22

Потому что в этом случае она инлайниться, а инлайн функции как известно должны быть объявлены во всех translation unit-ах.

Добавлено через 59 секунд
Цитата(Стандарт)

An inline function shall be defined in every translation unit in which it is used.

Автор: zim22 17.7.2009, 16:30
Цитата(azesmcar @  17.7.2009,  16:19 Найти цитируемый пост)
Потому что в этом случае она инлайниться, а инлайн функции как известно должны быть определены во всех translation unit-ах.

т.е. можно переопределять её тело, как это ни печально?
и во время объявления класса в main.cpp я изменил его члены данных (определил value типа int) - это тоже прокатило...
т.е. это уже два разных класса получается, а не один. хотя и не совсем два - т.к. конструктор у них один - общий...
ужас какой-то получается

Код

class A {
public:
  A(); 
  void fcn() { int x = 5; }
  int value;  
};

int main()
{  
   A t;
   t.fcn();   
   t.value = 20;

  return 0;
}


Код

// A.cpp
class A
{
public:
  A();
  void fcn(){int y = 10; }
private:
  int data;
};

A::A() : data(99) { }



Автор: azesmcar 17.7.2009, 16:37
Цитата(zim22 @  17.7.2009,  16:30 Найти цитируемый пост)
определены 

точно smile опять эти слова спутал

Цитата(zim22 @  17.7.2009,  16:30 Найти цитируемый пост)
ужас какой-то получается

а вот насчет этого ужаса
Цитата

An inline function shall be defined in every translation unit in which it is used and shall have exactly the
same definition in every case 
(3.2).

так что то, что это безобразие работает где-то еще ничего не значит smile

Автор: zim22 17.7.2009, 16:48
ок. с inline функциями разобрался  smile 
как насчёт этого:
Цитата(zim22 @  17.7.2009,  16:30 Найти цитируемый пост)
и во время объявления класса в main.cpp я изменил его члены данных (определил value типа int) - это тоже прокатило...т.е. это уже два разных класса получается, а не один. хотя и не совсем два - т.к. конструктор у них один - общий...


Автор: azesmcar 17.7.2009, 16:52
zim22

Ну на С++ есть множество способов извратиться, потому этот язык и считается сложным и именно потому для него написано столько книг. Просто не надо так извращаться smile 

Автор: Леопольд 17.7.2009, 17:03
Вот это тоже работает, в VC 2008 Express, а не должно:

test.cpp
Код

#include <iostream>

struct S{
private:
    int value;
public:
    S(void);
    int fnc(void){
        return value;
    }
    void mem_fun(void);
};

S::S(void):value(5) {}

void S::mem_fun(void){
    std::cout<<fnc()<<std::endl;
}


main.cpp
Код

class S{
    int value;
public:
    S(void);
    int fnc(void){
        return 10;
    }
    void mem_fun(void);
};


int main(int argc, char* argv[])
{
    S().mem_fun();
    return 0;
}


Но при этом выводит 5 в консольке. Не удивлюсь, если g++ откажется это собирать... Не удивлюсь, если соберёт, и выведет 10 smile или 5  smile 

Автор: zim22 17.7.2009, 17:08
Цитата(Леопольд @  17.7.2009,  17:03 Найти цитируемый пост)
Вот это тоже работает, в VC 2008 Express:

а почему бы ему и не работать. как по мне - обычный код smile
Цитата(Леопольд @  17.7.2009,  17:03 Найти цитируемый пост)
Но при этом выводит 5 в консольке.

он и должен 5 выводить. по крайней мере я в уме пошагово прошёл - 5 на выходе оказалось smile
Код

int main(int argc, char* argv[])
{
    S obj; // конструктор инициализирует value "пятёркой"
    obj.mem_fun(); // вызывается функция mem_fun из test.cpp. 
                            // она выводит value на экран. а оно равно пяти.
    return 0;
}

Автор: Леопольд 17.7.2009, 17:10
Цитата(zim22 @ 17.7.2009,  17:08)
Цитата(Леопольд @  17.7.2009,  17:03 Найти цитируемый пост)
Вот это тоже работает, в VC 2008 Express:

а почему бы ему и не работать. как по мне - обычный код smile

По стандарту, это не должно скомпилироваться.

Автор: zim22 17.7.2009, 17:14
Цитата(Леопольд @  17.7.2009,  17:03 Найти цитируемый пост)
Не удивлюсь, если g++ откажется это собирать... 

собирает. без единого warning'a
Цитата(Леопольд @  17.7.2009,  17:10 Найти цитируемый пост)
По стандарту, это не должно скомпилироваться.

ссылочкой не поделитесь где это в стандарте?
***
приведенные вами два cpp файла аналогичны нижеизложенному коду, за одним исключением, в котором вы сами нарушили стандарт:
переопределили тело инлайн функции.

Код

#include "test.h"

int main()
{
  S().mem_fun();
  return 0;
}

Код

// test.h
#ifndef TEST_H
#define TEST_H

struct S{
private:
    int value;
public:
    S(void);
    int fnc(void){
        return value;
    }
    void mem_fun(void);
};


#endif // TEST_H

Код

// test.cpp
#include "test.h"
#include <iostream>

S::S(void):value(5) {}

void S::mem_fun(void){
    std::cout<<fnc()<<std::endl;
}


Автор: Леопольд 17.7.2009, 17:20
Цитата(zim22 @ 17.7.2009,  17:14)
ссылочкой не поделитесь где это в стандарте?

azesmcar уже цитировал стандарт

Попробуйте заменить main вот на это smile

Код

int main(int argc, char* argv[])
{
    S obj;
    obj.mem_fun();
    std::cout<<obj.fnc()<<std::endl;
    return 0;
}


А потом на это
Код

int main(int argc, char* argv[])
{
    S obj;
    obj.mem_fun();
//  std::cout<<obj.fnc()<<std::endl;
    return 0;
}


надо будет добавить в main #include <iostream>

Автор: zim22 17.7.2009, 17:23
Цитата(Леопольд @  17.7.2009,  17:20 Найти цитируемый пост)
azesmcar уже цитировал стандарт

прочитайте ещё раз то, что он цитировал
Цитата(azesmcar @  17.7.2009,  16:37 Найти цитируемый пост)
An inline function shall be defined in every translation unit in which it is used and shall have exactly thesame definition in every case (3.2).

shall != must

Автор: Леопольд 17.7.2009, 17:26
Цитата(zim22 @ 17.7.2009,  17:23)
shall != must

Однако ворнинга нет...  А значит, компилятор забил на этот shall smile Я думаю, это бага в gcc 4.3.3

Добавлено @ 17:30
А вот это даёт разный результат в VC 2008 Express и в gcc 4.3.3 - компиляторам нельзя верить, надо проверять smile В этом случае, я думаю, они оба отсупили от стандарта. Даже ворнинга нет, хотя, по моему, это вообще не должно компилироваться. Остаётся только считать, то это даёт нам undefined behaviour.

Код

int main(int argc, char* argv[])
{
    S().mem_fun();
    std::cout<<S().fnc()<<std::endl;
    return 0;
}

Автор: zim22 17.7.2009, 17:34
Цитата(Леопольд @  17.7.2009,  17:26 Найти цитируемый пост)
Однако ворнинга нет...  А значит, компилятор забил на этот shall

да, глюк неприятный. undefined behavior. однако избежать его достаточно просто - не переопределять inline функции smile

Автор: Леопольд 17.7.2009, 17:38
Цитата(zim22 @ 17.7.2009,  17:34)
да, глюк неприятный. undefined behavior. однако избежать его достаточно просто - не переопределять inline функции smile

По старинке, пользоваться #include  smile 

Автор: mes 17.7.2009, 20:17
Цитата(zim22 @  17.7.2009,  14:53 Найти цитируемый пост)

ну а почему с функциями прокатывает? 

потому что Вы путаете сущности  этапа компиляции и линковки. Для начала исходный код, транслируется в объектный - этап компиляции.
На этом этапе Cpp-код  лишается всего своего ООП . В итоге объектники cpp, си, паскаля, асма и др. "выглядят" одинаково и из нескольких таких (полученных с разных языков) объектников можно слинковать одну общую прогу. 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)