Наследование в C ++ - PullRequest
       8

Наследование в C ++

1 голос
/ 22 сентября 2011

РЕДАКТИРОВАТЬ 2:

Вот простое резюме того, что я хочу сделать (я думаю) : Я хочу динамически создавать глобальные экземпляры на основе условий, которые рассчитываются во время выполнения.

Вы можете перейти к EDIT1, если хотите взглянуть на пример кода, но на данный момент текст, выделенный жирным шрифтом, является, вероятно, самым простым для понимания ...

КОНЕЦ РЕДАКТИРОВАНИЯ 2.

Мой вопрос о полиморфизме и наследовании. В частности, я хочу знать, есть ли способ унаследовать функции и указатели от другого класса.

У меня есть класс с именем Globals, который содержит различные указатели на объекты для других классов, а также различные функции. Вместо того, чтобы копировать / вставлять код, я напишу простой пример:

(для простоты и чистоты я убрал защиту заголовков)

Ниже приведены мои globals.h и globals.cpp соответственно:

// Example of globals.h
#include <iostream>
#include <cstdio>
using namespace std;

class Globals {
  public:
    Globals ();
    virtual ~Globals ();

    void function1(char*);
    void function2();

    class Input *input;
    class Error *error;

};

// Example of globals.cpp
#include "globals.h"
Globals::Globals()
{
    input = new Input();
    error = new Error();
}

void Globals::function1(char*nm)
{
    cout << nm << endl;
}

Теперь, в моем коде для моего Input класса, скажем, я хочу использовать метод function1(char*), возможно ли это без передачи объекта в класс Input? Под этим я подразумеваю, что в настоящее время мой класс Input передается объекту *globals, поэтому я мог бы вызвать функцию следующим образом: globals->function2();. Но это может стать очень грязным, если у меня много функций в разных классах. Кроме того, есть ли способ использовать указатель Error на объект, инициализированный в Globals? Если Error имеет функцию с именем error_func(), как я могу вызвать ее так: error->error_func() из моих Input функций?

Спасибо, и я прошу прощения, если я был слишком запутанным в моем вопросе. Я буду рад уточнить, если нужно.

Amit

РЕДАКТИРОВАТЬ 1: Добавлен упрощенный код для более четкого представления того, что я хочу сделать

// Example of globals.h
#include <iostream>
#include <cstdio>
#include "input.h"
#include "error.h"

using namespace std;


class Globals {
  public:
    Globals ();
    virtual ~Globals ();

    class Input *input;
    class Error *error;

};

// Example of globals.cpp
#include "globals.h"
Globals::Globals()
{
    input = new Input();
    error = new Error();
}

// Example of input.h
#include "globals.h"
class Input {
    public:
        Input();
        virtual ~Input();
}

// Example of input.cpp
#include "globals.h"
Input::Input()
{
    error->print("Hello\n"); // <-- THIS is really what I want to accomplish (without being sent a globals object and say globals->error->print();
}

// Example of error.h
#include "globals.h"
class Error {
    public:
        Error() { }
        virtual ~Error() { } 
        void print(char*);
}

// Example of error.cpp
#include "globals.h"
Error::print(char* nm)
{
    cout << nm << endl;
}

Ответы [ 3 ]

1 голос
/ 22 сентября 2011

Обновленный ответ :

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

// logger_interface.h
class LoggerInterface {
   public:
      virtual ~LoggerInterface() {}
      virtual void Log(const string& message) = 0;
   protected:
      LoggerInterface() {}
};

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

// logger_factory.h
LoggerInterface* CreateLogger(LoggerOptions options);

При реализации фабрики мы скрываем различные реализации:

// logger_factory.cc
class DoNotLogLogger : public LoggerInterface {
   public:
      DoNotLogLogger() {}
      virtual ~DoNotLogLogger() {}
      virtual void Log(const string& message) {}
};

class LogToStdErrLogger : public LoggerInterface {
   public:
      LogToStdErrLogger() {}
      virtual ~LogToStdErrLogger() {}
      virtual void Log(const string& message) {
         std::cout << message << std::endl; 
       }
};

LoggerInterface* CreateLogger(LoggerOptions options) {
    if (options.IsLoggingEnabled() && options.ShouldLogToStdErr()) {
      return new LogToStdErrLogger;
    }
    return new DoNotLogLogger;
}

Нет причины, по которой объект, который вы динамически создаете таким образом, должен быть глобальным; на самом деле, сделать его глобальным - очень плохая идея. Просто создайте его там, где вам нужно, и передайте его в качестве параметра нужным функциям.

Оригинальный ответ :

Наследование не то слово, которое вы ищете. По сути, вы запрашиваете static function:

class ClassName {
   public:
       static void methodName();
};

В приведенном выше примере methodName можно вызвать с помощью ClassName::methodName(), не требуя конкретного экземпляра класса с именем ClassName. Тем не менее, если вы хотите сделать это, это больше согласуется с соглашениями стиля C ++, чтобы сделать его автономной функцией в пространстве имен, например:

namespace name_of_namespace {
void functionName();
}

Вышеуказанное вызывается с использованием name_of_namespace::functionName(), как в предыдущем примере, за исключением того, что префикс легче изменить или удалить (например, с помощью директивы using).

ПРИМЕЧАНИЕ: с точки зрения проектирования, вы должны использовать отдельно стоящую или статическую функцию, только если она не зависит от какого-либо состояния (кроме переданных ей параметров) и нет возможности альтернативных реализаций. Как только есть состояние или альтернативные реализации, вы действительно должны передать объект, инкапсулирующий это состояние, даже если это трудная задача, поскольку передача объекта облегчает его настройку, облегчает макетирование в тестах. и позволяет избежать проблем с многопоточностью.

1 голос
/ 22 сентября 2011

Я представляю, что у вас есть такая ситуация:

struct A {
  void f();
};

struct B {
  void g();
};

struct C : virtual A, virtual B {
  C(A *ap, B *bp) 
    : A(ap), B(bp)  // This doesn't actually work -- theoretical
  {
  }

  void h() 
  { 
     f(); // calls A::f()
     g(); // calls B::g();
  }
};

Обычно, когда вы создаете C, вы создаете новые As и B, но вы хотели бы повторно использовать существующиевместо этого, но все равно рассматривайте это как наследование, так что вам не нужно явно указывать, какой объект вызывать.

К сожалению, C ++ не поддерживает это.Есть несколько вариантов:

Вы можете создать прокси-классы, которые откладывают вызовы функций:

struct AProxy {
  AProxy(A *ap) : a(*ap) { }
  void f() { a.f(); }

  A &a;
};

struct BProxy {
  BProxy(B *bp) : b(*bp) { }
  void g() { b.g(); }

  B &b;
};

struct C : AProxy, BProxy {
  C(A *ap,B *bp) : AProxy(ap), BProxy(bp) { }

  void h()
  {
    f(); // calls AProxy::f() which calls a.f()
    g(); // calls BProxy::g() which calls b.g()
  }
};

Это может помочь, если вы используете A и B в разных местах.

Если вместо этого у вас не много классов, а много вызовов f () и g (), вы можете просто сделать это:

struct C {
  C(A *ap,B *bp) : a(*ap), b(*bp) { }
  void f() { a.f(); }
  void g() { b.g(); }
  void h1() 
  {
    f();  // well at least the call is clean here
    g();
  }
  void h2()
  {
    f(); // and clean here
    g();
  }

  A &a;
  B &b;
};

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

1 голос
/ 22 сентября 2011

Если я правильно понимаю ваш вопрос, функции автоматически «наследуются», по крайней мере, для тех целей, которые вам нужны.

Например, ваш глобальный класс имеет два метода, function1(char*) и function2(). Если вы делаете класс:

class Descendent
    : public Global
{  };

int main()
{
    Global * global = new Global();
    Global * desc = new Descendant();
    char * str = "string";

    // These two will run the same function:
    global->function1(str);
    desc->function1(str);
}

Чтобы предотвратить это (функции, вызываемые на основе текущего типа), вы должны использовать виртуальный, например:

class Global
{
    virtual void function1(char *);
};

class Descendant
{
    virtual void function1(char *);
};

int main()
{
    Global * global = new Global();
    Global * desc = new Descendant();
    char * str = "string";

    // These two will NOT run the same function:
    global->function1(str);
    desc->function1(str);
}

Теперь я не совсем уверен, но здесь может пригодиться идиотная единичная форма, в зависимости от того, насколько глобальным является Global. В этом случае у вас будет глобальный вид:

class Global
{
    static Global * GetSingleton()
    {
        if (!Global::m_Instance) Global::m_Instance = new Global();
        return Global::m_Instance;
    }

    void function1(char *);

    static Global * m_Instance;
};


class Descendant
{
    void function1(char *)
    {
        Global * global = Global::GetGetSingleton();
        // ...
    }
};

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...