Почему и где мы используем литье? - PullRequest
2 голосов
/ 04 октября 2009

Есть ли случаи, когда мы выполняем приведение объектов вниз?

Если мы делаем, почему?

Я видел способ скрыть реализацию, используя приведенный ниже код. Это правильный способ сделать? Есть ли лучший способ добиться того же.

class A{
    public:
      A();
      virtual ~A();
      //exposed virtual functions
};

class AImpl : public A{
    public:
       AImpl(A *obj);
       virtual ~AImpl();     
      //exposed virtual functions++
};

class Helper{ //utility class so i am making constructor and assignment operator as private
    public:
       static bool doWork(A *obj){
          AImpl *objImpl = dynamic_cast<AImpl *> (obj);
          return doWork(objImpl); // some internal function
       }

    private:
         Helper();
         Helper(const Helper& obj);
         const Helper& operator=(const Helper& obj);
};

Вопрос все еще не имеет смысла. Согласен. Я до сих пор не нашел правильного способа скрыть детали реализации от клиента.

UncleBens Я назвал эту вещь неправильно как нарезка объектов. По сути, я имел в виду это (нарезка объектов), поскольку информация, относящаяся к производной части, отсутствует.

S.Soni Спасибо за прекрасное объяснение. Теперь я действительно могу задать вопрос.

Рассмотрим перспективы клиентов. Единственный класс, который виден ему, это класс A и класс Helper (потому что у меня есть скрытая реализация за AImpl

Клиент должен написать следующий код, так как он не знает о классе AImpl

int main(){
    A *a = new A();
    Helper.doWork(a);
    // ...
}

Как вы сказали, AImpl * в этом случае будет фактически указывать на объект базового класса, что неправильно (вы объяснили это на отличном примере), поэтому такой подход скрытия реализации не является правильным.

Любая попытка получить доступ к производной функции-члену класса приведет к падению (и это правильно).

Как мне скрыть реализацию в этом случае? Это проблема дизайна сейчас?

Ответы [ 5 ]

2 голосов
/ 04 октября 2009
**Are there any cases where we do down casting of objects**

Целью dynamic_cast является выполнение приведения к полиморфным типам. За Например, с учетом двух полиморфных классов Band D, с D, полученным из B, a dynamic_cast всегда может привести указатель D * к указателю B *. Это потому что база Указатель всегда может указывать на производный объект. Но dynamic_cast может привести указатель B * в указатель D *, только если объект, на который указывает объект, фактически является объектом D.

**`Is there any better way to achieve the same`**

Возможно, самым важным из новых операторов приведения является dynamic_cast. dynamic_cast выполняет приведение во время выполнения, которое проверяет правильность преобразования.

1) Ваш класс не является полиморфным. Класс, который объявляет или наследует виртуальную функцию, называется полиморфным классом

2) Синтаксис dynamic_cast: dynamic__cast (expr)

1-е редактирование:

Попробуй вот так, будет работать

class A
{
    public:
      A();
      virtual ~A();// Notice here i have put virtual
};

class AImpl : public A
{
    public:
       AImpl(A *obj);
       ~AImpl();     
};

class Helper
{
    public:
        Helper(){}
       static bool doWork(A *obj)
       {
          AImpl *objImpl = dynamic_cast<AImpl*> (obj);
          return true;
       }
};

Изучите этот пример:

class Base
{
public:
    virtual void f() { cout << "Inside Base\n"; }
    // ...
};
class Derived: public Base 
{
public:
    void f() { cout << "Inside Derived\n"; }
};


int main()
{  
    Base *bp, b_ob;
    Derived *dp, d_ob;
    dp = dynamic_cast<Derived *> (&d_ob);
    if(dp) {
        cout << "Cast from Derived * to Derived * OK.\n";
        dp->f();
    } else

        cout << "Error\n";
    cout << endl;
    bp = dynamic_cast<Base *> (&d_ob);
    if(bp) {
        cout << "Cast from Derived * to Base * OK.\n";
        bp->f();
    } else
        cout << "Error\n";
    cout << endl;
    bp = dynamic_cast<Base *> (&b_ob);
    if(bp) {
        cout << "Cast from Base * to Base * OK.\n";
        bp->f();
    } else
        cout << "Error\n";
    cout << endl;
    dp = dynamic_cast<Derived *> (&b_ob);
    if(dp)
        cout << "Error\n";
    else
        cout << "Cast from Base * to Derived * not OK.\n";
    cout << endl;
    bp = &d_ob; // bp points to Derived object
    dp = dynamic_cast<Derived *> (bp);
    if(dp) {
        cout << "Casting bp to a Derived * OK\n" <<
            "because bp is really pointing\n" <<
            "to a Derived object.\n";
        dp->f();
    } else
        cout << "Error\n";
    cout << endl;
    bp = &b_ob; // bp points to Base object
    dp = dynamic_cast<Derived *> (bp);
    if(dp)
        cout << "Error";
    else {
        cout << "Now casting bp to a Derived *\n" <<
            "is not OK because bp is really \n" <<
            "pointing to a Base object.\n";
    }
    cout << endl;
    dp = &d_ob; // dp points to Derived object
    bp = dynamic_cast<Base *> (dp);
    if(bp) {
        cout << "Casting dp to a Base * is OK.\n";
        bp->f();
    } else
        cout << "Error\n";
    return 0;
}
1 голос
/ 04 октября 2009

Хорошо, если верно следующее (не единственные действительные причины, но распространенная причина):

  • у вас есть MVC-подобная архитектура
  • вы хотите скрыть детали реализации из клиентского кода (no-na ...)
  • вам нужно передать некоторые ссылки из ядра в код клиента (дескрипторы всех видов и т. Д.)
  • единственная допустимая реализация открытых интерфейсов должна быть в ядре MVC, часть

тогда использование этого способа довольно распространено.

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

1 голос
/ 04 октября 2009

Из вашего кода и информации о том, что переданный указатель фактически указывает на A, а не AImpl, а также конструктор AImpl, который принимает A *, я понимаю, что вы этого хотите:

class Helper{
    public:
       static bool doWork(A *obj){
          AImpl objImpl(obj); //construct an AImpl instance, using available constructor
          return doWork(&objImpl); // some internal function
       }
};

Невозможно привести базовый экземпляр к экземпляру производного класса (откуда будет отсутствующая производная часть ??).

1 голос
/ 04 октября 2009

Ваш пример кода содержит не менее 4 синтаксических ошибок, поэтому сложно судить о том, что вы пытаетесь сделать.

И логически это тоже не сработает. У вас есть класс AImpl, который наследует A, затем функция-член, которая принимает A и, по-видимому, пытается динамически привести ее к AImpl. Но это не AImpl, потому что это просто A, так как именно так объявляется параметр. Если вы передадите экземпляр AImpl в эту функцию, он будет сокращен до A.

Вы можете сделать это ссылкой или указателем на A, а затем это может быть AImpl. Динамические приведения используются только для ссылок или указателей.

Приведение вниз используется, когда у нас есть переменная или параметр, который имеет статический тип базового класса, но мы логически знаем, что это (или может быть) производного класса. Этого избегают везде, где это возможно, потому что это означает, что процесс компиляции не может полностью проверить правильность типа программы, т.е. ответ на вопрос «пытаетесь ли вы поместить квадратный колышек в круглое отверстие» не может быть полностью дан ответ до времени выполнения.

Обновление после редактирования вопроса

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

class A
{
    friend class LibraryThing;

    void visibleToLibraryThing();

public:
    // ctor, etc.

    void visibleToAll();
};

class LibraryThing
{
public:
    void foo(A &a)
    {
        a.visibleToLibraryThing();
    }
};

Класс LibraryThing может получить доступ к закрытым членам A, поскольку он объявлен в качестве друга A.

Недостатком является то, что LibraryThing может получить доступ к всему в A, поэтому это означает, что, как автор библиотеки, вы не сможете извлечь выгоду из инкапсуляции. Только пользователи вашей библиотеки.

0 голосов
/ 05 октября 2009

Вы можете использовать PIMPL, чтобы скрыть детали реализации, маскировать зависимости и ускорить сборку.

http://www.gotw.ca/gotw/028.htm

http://www.ddj.com/cpp/205918714

http://www.gotw.ca/gotw/024.htm

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