LINUX.ORG.RU

Непонятно, откуда берется multiple definition

 


1

1

В отдельном хидере определены 2 константы:

#ifndef STRINGS_H
#define STRINGS_H

const char * Started = "started";
const char * Stopped = "stopped";

#endif//STRINGS_H
После этого они используются 2 двугих хидерах:
#ifndef START_H
#define START_H
#include <cstdio>
#include "strings.hh"
void start()
{
    puts( Started );
}
#endif
#ifndef STOP_H
#define STOP_H
#include <cstdio>

#include "strings.hh"
void stop()
{
    puts( Stopped );
}
#endif
Непонятно почему при сборке раз за разом вылетает ошибка (.data+0x0) error: multiple definition of `Started', и то же самое со stopped. Единственное, что еще м.б. - корявый Makefile:
all:
	g++ start.cc stop.cc 12.hh act.cc one.cc task1.cc two.cc Worker.cc act.hh Cat.hh Dog.hh Egg.hh Pet.hh start.hh stop.hh strings.hh Worker.hh Zoo.hh -o task1
В конце сборки идет сообщение «error: collect2: выполнение ld завершилось с кодом возврата 1»

Похоже на ситуацию, когда не ставишь проверку в начале хидера, и он включается несколько раз, но здесь это есть. М.б. надо сделать сборку объектов отдельно, потом уже из них общий бинарник? Или проблему следует искать всё-таки в коде?

PS: ошибка идет при включении хидеров со стартом и стопом. Задачу решить надо всё-таки мне, поэтому полностью использование приводить не буду. Но пока тупик, не понимаю даже в какой области ошибка

★★★★

Если в один файл с исходниками заинклудить start.h, а в другой — stop.h, то получится 2 объявления символа с одинаковым именем в разных объектных файлах, и линкер выдаст ошибку. Решение: в strings.hh написать:

#ifndef STRINGS_H
#define STRINGS_H

extern const char * Started;
extern const char * Stopped;

#endif//STRINGS_H

В strings.cc:

const char * Started = "started";
const char * Stopped = "stopped";
gentoo_root ★★★★★ ()
Ответ на: комментарий от unanimous

Ха-ха-ха, еще один не знает про include guards/pragma once

Мухаха, ещё один не знает про компоновку

Begemoth ★★★★★ ()

const char *

Судя по этой записи ты хочешь объявить константы. Для констант в С++ компоновка по умолчанию - внутренняя и выносить их определения в один из файлов исходников не требуется. Но тут ты объявляешь не константу, а глобальную переменную - указатель на константную строку, чтобы объявить константный указатель, то следует поместить const и после *:

const char * const Started = "started";
const char * const Stopped = "stopped";
Begemoth ★★★★★ ()
Ответ на: комментарий от Begemoth

И ещё один вариант, не допускающий неоднозначностей в понимании синтаксиса типов в С++:

#include <type_traits>

const std::add_pointer<const char>::type Started = "started";
std::add_const<std::add_pointer<std::add_const<char>::type>::type>::type Stopped = "stopped";
Begemoth ★★★★★ ()
Ответ на: комментарий от Begemoth

Очуметь) Это надо вдумчиво переваривать

А с чем м.б. связано, что в клиентском коде компилятор ругается на методы класса(не на поля), undefined reference, хотя реализация есть и хидер подключен?

wingear ★★★★ ()
Ответ на: комментарий от wingear

А с чем м.б. связано, что в клиентском коде компилятор ругается на методы класса(не на поля), undefined reference, хотя реализация есть и хидер подключен?

Код в студию.

Begemoth ★★★★★ ()
Ответ на: комментарий от wingear

Очуметь) Это надо вдумчиво переваривать

Не-не, это тебе пока рано, а вот синтаксис объявления типов - вдучиво изучи.

Begemoth ★★★★★ ()
Ответ на: комментарий от Begemoth
//chicken.hh

#ifndef CHICKEN_H
#define CHICKEN_H
//#include "Egg.hh"
class Egg;

class Chicken {
public:
    Egg getCreator();
    Egg * creator;
};
#endif
//chicken.cc

#include "Chicken.hh"
#include "Egg.hh"
Egg Chicken::getCreator(){
    if (!creator) exit(1);
    return *creator;
}
//egg.hh

#ifndef EGG_H
#define EGG_H
//#include "Chicken.hh"
class Chicken;

class Egg {
public:
    Chicken * creator;
    Chicken getCreator();
};
#endif
//egg.cc

#include "Egg.hh"
#include "Chicken.hh"
Chicken Egg::getCreator()
{
    if (!creator) exit(1);
    return *creator;
}
//zoo.h
namespace Zoo {
	#include "Cat.hh"
	#include "Dog.hh"
    #include "Chicken.hh"
	#include "Egg.hh"
}
//main.cc
#include "zoo.h"
using namespace Zoo;
...
Chicken chicken;
    Egg egg;
    chicken.creator = &egg;
    egg.creator = &chicken;
    Egg e = chicken.getCreator();
    //error: undefined reference to `Zoo::Chicken::getCreator()'
    Chicken c = egg.getCreator();
    //error: undefined reference to `Zoo::Egg::getCreator()'
    if( chicken.creator == c.creator
        && egg.creator == e.creator )
    {
        std::cout << "the `Chicken and Egg' dilemma is solved!" << std::endl;
    }
...
wingear ★★★★ ()
Ответ на: комментарий от wingear
namespace Zoo {
	#include "Cat.hh"
	#include "Dog.hh"
    #include "Chicken.hh"
	#include "Egg.hh"
}

Жжёшь напалмом. Пространство имён - неотемлимая часть имени класса и функции. В main ты используешь тип ::Zoo::Chicken, а в chicken.cc реализуешь класс ::Chicken, они никак не связаны между собой. Все классы сразу объявляй в пространстве имён Zoo, при определении функций-членов, также про него не забывай: Zoo::Chicken Zoo::Egg::getCreator()

Begemoth ★★★★★ ()
Ответ на: комментарий от LongLiveUbuntu

Тут всё упирается в то, нужны ТС глобальные переменные или константы, константы в С++ по умолчанию static, но их надо правильно объявить :-)

Begemoth ★★★★★ ()
Ответ на: комментарий от Begemoth

Кстати я вот этого const char* const... никогда не понимал, как и Bar() : Foo { } в конструкторах и int method() const... Может разъяснишь как правильно это читать?

LongLiveUbuntu ★★★★★ ()
Ответ на: комментарий от Begemoth

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

wingear ★★★★ ()
Ответ на: комментарий от Begemoth

т.е. убрать namespace из Zoo.h, а все входящие в него хидеры поправить на

namespace Zoo {...}
?

Пробовал, в конечном итоге тоже самое. Или ничего не надо менять в хидерах, а в релизации писать


#include "Chicken.hh"
#include "Egg.hh"
Zoo::Egg Zoo::Chicken::getCreator(){
    if (!creator) exit(1);
    return *creator;
}
? Сейчас так, но тоже самое. Или нужно как-то так в хидере:

#ifndef CHICKEN_H
#define CHICKEN_H
//#include "Egg.hh"
namespace Zoo
{
class Egg;
}
class Chicken {
public:
    Zoo::Egg getCreator();
    Zoo::Egg * creator;
};

#endif

wingear ★★★★ ()
Ответ на: комментарий от LongLiveUbuntu

const char* const...

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

Bar() : Foo { } в конструкторах

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

int method() const...

Эта функция может быть применена к константному объекту, т.е. тип выражения которое порождает этот объект или ссылку или указатель на него - const T, const T&, const T*. Этот const в сигнатуре функции, указывает, что this в этой функции будет иметь тип const T*. Другие функции не могут быть применены к константному объекту.

Begemoth ★★★★★ ()
Ответ на: комментарий от wingear

т.е. убрать namespace из Zoo.h, а все входящие в него хидеры поправить на namespace Zoo {...}

Да, а то у тебя получлись разные классы - используешь один, а реализуешь другой.

убрал отовсюду namespace, тоже самое. Кажется, не в нем дело
В Makefile не включил файлы, где была реализация.

Ну вот две ошибки было - неправильное использование пространств имён и ошибка при сборке.

Begemoth ★★★★★ ()
Ответ на: комментарий от Begemoth

Спасибо ^__^

Правда я мало что понял из этих объяснений, но все же...

//Вот поэтому я выбрал Java.

LongLiveUbuntu ★★★★★ ()
Ответ на: комментарий от unanimous

Ха-ха-ха, еще один не знает про include guards/pragma once

Глаза протрите, есть там guards. А вот про линковку стоило бы почитать.

andreyu ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.