我们为什么以及在哪里使用羽绒铸造?

时间:2009-10-04 14:54:26

标签: c++ design-patterns oop

是否存在我们对物体进行投射的情况?

如果我们这样做,为什么?

我已经观察到使用以下代码隐藏实现的方法。这是正确的方法吗?有没有更好的方法来实现同样的目标。

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 个答案:

答案 0 :(得分:2)

**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)

第一次编辑:

尝试这样,它会起作用

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 :(得分:1)

您的示例代码至少包含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中的所有,因此这意味着作为库的作者,您将无法从封装中获益。只有您图书馆的用户才会。

答案 2 :(得分:1)

从你的代码和传入的指针实际指向A的信息,而不是AImpl,也是接受A *的构造函数AImpl,我收集你想要的东西:

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

没有办法强制基础实例到派生类的实例(缺少的派生部分来自哪里?)。

答案 3 :(得分:1)

好吧,如果以下情况属实(不是唯一有效的理由,而是一个常见理由):

  • 你有像建筑一样的MVC
  • 您想隐藏客户端代码中的实现详细信息(no-na ...)
  • 您需要将一些引用从核心传递到客户端代码(各种句柄等)
  • 公共接口的唯一有效实现必须位于MVC部分的核心

然后使用这种方式很常见。

但是,如果它只是允许使用任何其他实现(因为只有你的库核心可以实现接口),那么声明类型也会很好;这将为调试器或崩溃转储提供一个很好的着陆,因为库的任何用户都应该以不应该这样做的方式搞乱它。当然,文档应该清楚地表明从A派生出来是一个坏主意。

答案 4 :(得分:0)

您可以使用PIMPL隐藏实现细节,屏蔽依赖关系并加快构建。

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

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

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