множественное определение: ошибка во время ссылки

Я пишу пару классов для упражнения по элементам управления копированием в учебнике для начинающих по C++.

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

вот класс сообщения (заголовки и файлы кода)

сообщение.h

#include <string>
#include "folder.h"

class message {

  friend void swap(message&,message&);

  public:
    explicit
    message(const std::string& =std::string());
    // copy control
    message(const message&);
    ~message();
    message &operator=(const message&);
    // interface
    void save(folder&);
    void remove(folder&);

  private:
    // data members
    std::string text;
    std::set<folder*> folders;
    // utility functions
    void add_to_folders(const message&);
    void remove_from_folders();

};

void swap(message &m1,message &m2)
{
  swap(m1.text,m2.text);
  swap(m1.folders,m2.folders);
}

message.cpp

#include "message.h"

message::message(const std::string &t) : text(t) {}

message::message(const message &other) : text(other.text),folders(other.folders) 
{
  add_to_folders(other);
}

message::~message() 
{
  remove_from_folders(); 
}

message& message::operator=(const message &other)
{
  remove_from_folders();
  text=other.text;
  folders=other.folders;
  add_to_folders(other);
  return *this;
}

void message::save(folder &f)
{
  folders.insert(&f); 
  f.addMsg(this); 
}

void message::remove(folder &f)
{
  folders.erase(&f);
  f.remMsg(this);
}

void message::add_to_folders(const message& m)
{
  for (auto i:m.folders)
    i->addMsg(this);
}

void message::remove_from_folders()
{
  for (auto i:folders)
    i->remMsg(this);
  folders.clear();  
}

а вот папка класс

folder.h

#include <set>

class message;

class folder {

  public:
    /*folder();
    folder(const folder&);
    ~folder();
    folder &operator=(const folder&);*/
    void addMsg(message*);
    void remMsg(message*);

  private:

    std::set<message*> messages;

};

folder.cpp

#include "folder.h"

void folder::addMsg(message *m)
{
  messages.insert(m);
}

void folder::remMsg(message *m)
{
  messages.erase(m);
}

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

main.cpp

#include "message.h"

int main()
{
  folder f1;
  message m1;
}

Что я делаю не так?


person Luca    schedule 10.09.2015    source источник


Ответы (3)


Вы включаете заголовочный файл message.h и в message.cpp, и в main.cpp. Поскольку у вас есть:

void swap(message &m1,message &m2)
{
  swap(m1.text,m2.text);
  swap(m1.folders,m2.folders);
}

В message.h теперь у вас есть функция подкачки, объявленная как в единице перевода main.cpp, так и в единице перевода message.cpp. Это вызовет множественную ошибку определения. Вам нужно переместить swap() в message.cpp

person NathanOliver    schedule 10.09.2015

Вы не забыли включить защиту в файлы заголовков?

#ifndef __MY_HEADER__
#define __MY_HEADER__

...

#endif

или если вам удобно использовать функции, выходящие за рамки стандарта С++:

#pragma once

...
person anorm    schedule 10.09.2015
comment
Я проверил, что они не повторяются, даже если я не устанавливал защиту заголовков в свой код. - person Luca; 10.09.2015
comment
Определения препроцессора не помогут ему в этой ситуации. - person Francis Cugler; 10.09.2015

Решение вашей проблемы очень простое: вы объявили void swap( data1, data2 ) в качестве дружественной функции в своем классе сообщений, а затем определили ее в нижней части вашего файла *.h. Выньте реализацию из файла *.h и поместите ее в файл message.cpp до того, как начнет функционировать любой из ваших классов. Убедитесь, что у вас есть определение (реализация) перед конструктором вашего класса в файле *.cpp, поскольку ваш объект класса будет полагаться на эту функцию подкачки. Это должно решить вашу проблему в процессе сборки. Да, каждый из трех файлов будет успешно скомпилирован, но решение не будет создано, потому что оно не может разрешить этот метод, поскольку он определен в вашем файле *.h. Кроме того, вы используете сообщение прототипа класса в своем файле folder.h, у вас также должен быть #include «message.h» в вашем файле folder.cpp, но для этого вам потребуются файлы *.h с #ifndef . .. #define... директивы #endif. Это должно вам помочь!

person Francis Cugler    schedule 10.09.2015