Подтвердить что ты не робот

Добавить метод в существующий класс С++ в другой файл

Возможно ли в C++ расширить класс (добавить метод) в другом исходном файле, не редактируя исходный исходный файл, в который записан класс?

В obj-c это возможно, написав еще один @interface AbcClass (ExtCategory)... @end

Я получил ошибки во время компиляции, когда я пытался что-то вроде этого:

//Abc.h
class Abc {            //This class is from a 3rd party library....
                       //  ...I don't want to edit its source file.
    void methodOne();
    void methodTwo();

}


//Abc+Ext.h
class Abc {       // ERROR: Redefinition of 'Abc'
    void methodAdded();
}

Как я могу добавить метод без создания производного класса? Моя цель - сохранить имя Abc и добавить к нему методы. У определенного класса в сторонней библиотеке, который я использовал, отсутствуют некоторые методы, и я хочу добавить эти методы, но я сохраняю исходный файл неотредактированным.

Есть ли способ сделать это? Я новичок в написании C++ кодов. Я знаком с некоторыми из его синтаксиса, но не знаю много.

4b9b3361

Ответ 1

Нет. Такое расширение класса невозможно в C++. Но вы можете унаследовать класс от исходного исходного файла и добавить новые функции в ваш исходный файл.

//Abc.h
class Abc {
    void methodOne();
    void methodTwo();
};


//Abc+Ext.h
class AbcExt : public Abc {       
    void methodAdded();
};

Затем вы можете вызывать методы следующим образом:

auto *obj = std::make_unique<AbcExt>();
obj->methodOne(); // by the virtue of base class
obj->methodAdded(); // by the virtue of derived class

Ответ 2

Есть способ на самом деле сделать это, но для этого требуется, чтобы компилятор поддерживал #include_next. GCC имеет это, не знаю о других компиляторах. Он также должен поддерживать хотя бы С++ 11.

Я бы точно не назвал этот трюк красивым, но он выполняет эту работу.

Убедитесь, что ваш путь включения имеет каталог, в котором находится файл расширения, перед каталогом, в котором находится исходный код (т.е. если исходный Abc.hpp находится в src, а затем переместите его на src/some_dir). Поэтому в этом случае ваши включенные dirs будут -Isrc -Isrc/some_dir.

Ваш код расширения должен быть в файле с тем же именем, что и исходный код. Итак, для этого примера Abc.hpp.

Здесь содержимое файла расширения:

#ifndef ABC_EXT_HPP_
#define ABC_EXT_HPP_

#include <utility>

namespace evil {
  // Search the include path for the original file.
  #include_next "Abc.hpp"
}

class Abc : public evil::Abc {
  public:
    /*
    // Inherit all constructors from base class. Requires GCC >=4.8.
    using evil::Abc::Abc;
    */

    /* Use the solution below if your compiler supports C++11, but not 
     * inheriting constructors.
     */
    template <class... Args>
    Abc (Args... args) : evil::ABC(std::forward<Args...>(args...)) { }

    ~Abc () { }

    void methodAdded () { /* Do some magic. */ }
};

#endif // ABC_EXT_HPP_

В примере отсутствуют такие вещи, как оператор присваивания, который не переадресуется базовому классу. Вы можете использовать тот же трюк, который используется для этого конструктора. Там могут быть другие недостающие вещи, но это должно дать вам отправную точку, которая достаточно хорошо работает для "простых" классов.

Одна вещь, которую мне не нравится, это создание "злого" пространства имен. Однако анонимные пространства имен не могут помочь здесь, потому что в каждой единицы перевода будет создано новое анонимное пространство имен, которое включает Abc.hpp. Это приведет к проблемам, если ваш базовый класс, например, статические элементы.

Изменить: Nevermind, оператор присваивания (т.е. Abc bla = evil::Abc(9)) также работает, потому что evil:Abc может быть неявно преобразован в Abc, потому что этот конструктор существует.

Изменить 2:. При наличии вложенных пространств имен может возникнуть множество проблем. Это происходит, как только есть #include в исходном Abc.hpp, потому что теперь он будет вложен в пространство имен evil. Если вы знаете все входящие, вы можете включить их перед объявлением пространства имен evil. Вещи становятся настоящими уродливыми, реальными быстрыми темпами.

Ответ 3

Нет конкретного механизма для этого непосредственно в текущем С++, но есть несколько способов добиться чего-то подобного ценой работы некоторых котельных:

Способ 1:

// foo.h
class Foo {
private:      // stuff
public:       // stuff

private:
    // All this crap is private. Pretend like I didn't expose it.
    // yeah, I know, you have to compile it, and it probably adds
    // dependencies you don't want to #include, like <string>
    // or boost, but suck it up, cupcake. Stroustrup hates life.
    void internalHelper(std::string&, std::vector&, boost::everything&);
};

Способ 2:

// foo.h
class Foo {
private:      // stuff
public:       // stuff
};

// fooimpl.h
// Internal file, do not export with the API.
class FooImpl : public Foo {
private:      // stuff
public:       // stuff
    // So yeah, you have to go thru a cast and an extra include
    // if you want to access this. Suck it up, cupcake.
    void internalHelper(std::string&, std::vector&, boost::everything&);
};

Способ 3:

// foo.h
class Foo {
private:      // stuff
public:       // stuff

    // For the private api: this is the worst approach, since it
    // exposes stuff and forces include/cruft on consumers.
    friend void foo_internalHelper(std::string&, std::vector&, boost::everything&);
};

// foo.cpp

// don't make it static or anyone can make their own as a way to
// back door into our class.
void foo_internalHelper(...);

Способ 4:

// foo.h
class Foo {
private:      // stuff
public:       // stuff

    // No dependencies, but nothing stops an end-user from creating
    // a FooPrivate themselves...
    friend class FooPrivate;
};

// foo1.cpp
class FooPrivate {
public:
    void fooInternalHelper(Foo* f) {
       f->m_privateInternalYouCantSeeMe = "Oh, but I can";
    }
};

Ответ 4

Вы не можете продлить класс Abc, точка!

Единственный выход - это автономные функции, такие как

Abc add(const Abc& a, int b);