【问题标题】:Upcasting pointer reference向上转换指针引用
【发布时间】:2009-12-22 21:01:33
【问题描述】:

我有以下人为的例子(来自真实代码):

template <class T>
class Base {
public:
 Base(int a):x(a) {}
    Base(Base<T> * &other) { }
    virtual ~Base() {}
private:
 int x;
};

template <class T>
class Derived:public Base<T>{
public:
  Derived(int x):Base<T>(x) {}
  Derived(Derived<T>* &other): Base<T>(other) {}

};


int main() {
 Derived<int> *x=new Derived<int>(1);
 Derived<int> y(x);
}

当我尝试编译它时,我得到:

1X.cc: In constructor ‘Derived<T>::Derived(Derived<T>*&) [with T = int]’:
1X.cc:27:   instantiated from here
1X.cc:20: error: invalid conversion from ‘Derived<int>*’ to ‘int’
1X.cc:20: error:   initializing argument 1 of ‘Base<T>::Base(int) [with T = int]’

1) 显然 gcc 被构造函数弄糊涂了。如果我删除参考 从构造函数,然后代码编译。所以我的假设是出了点问题 带有向上转换的指针引用。谁能告诉我这是怎么回事?

2) 一个稍微不相关的问题。如果我要在构造函数中做一些可怕的事情,比如“删除其他”(请耐心等待), 当有人向我传递一个指向堆栈上某物的指针时会发生什么?

E.g. Derived<int> x(2);
     Derived<int> y(x);

where 

 Derived(Derived<T>*& other) { delete other;}

如何确保该指针合法地指向堆上的某物?

【问题讨论】:

    标签: c++ inheritance pointers reference upcasting


    【解决方案1】:

    Base&lt;T&gt;Derived&lt;T&gt; 的基本类型,但Base&lt;T&gt;* 不是Derived&lt;T&gt;* 的基本类型。可以传递派生指针代替基指针,但不能传递派生指针引用代替基指针引用。

    原因是,假设你可以,并且假设 Base 的构造函数要向引用中写入一些值:

    Base(Base<T> * &other) {
        Base<T> *thing = new Base<T>(12);
        other = thing;
    }
    

    您刚刚将一个指向不是Derived&lt;T&gt; 的指针写入指向Derived&lt;T&gt; 的指针。编译器不能让这种情况发生。

    【讨论】:

      【解决方案2】:
      1. 您不能将对指向 Derived 的指针的引用转换为对指向 Base 的指针的引用。 (此处的模板不会导致问题,因此从下面的示例中删除。)
      2. 如果您想推迟对指针的责任,请使用智能指针类型。智能指针类型可以代表原始指针不能的“删除责任”。示例包括 std::auto_ptr 和 boost::shared_ptr 等。

      为什么不能向上转换指针引用:

      struct Base {};
      struct Derived : Base {};
      struct Subclass : Base {};
      
      int main() {
        Derived d;
        Derived* p = &d;
        Derived*& d_ptr = p;
      
        Base*& b_ptr = d_ptr; // this is not allowed, but let's say it is
      
        Base b;
        b_ptr = &b; // oops! d_ptr no longer points to a Derived!
      
        Subclass s;
        b_ptr = &s; // oops! d_ptr no longer points to a Derived!
      }
      

      当您将“其他”参数传递给 Base ctor 时,您正在尝试执行与上述 b_ptr = d_ptr 相同的操作。

      【讨论】:

        【解决方案3】:

        您可以通过在文档中写入指针并依赖调用者遵守该指针来确保该指针指向堆上的某个内容。如果调用你的构造函数的人传递了一个堆栈指针,那么所有的赌注都被取消了,这不是你的错——你可以尝试及早发现问题,但不能保证。

        这就是标准库的工作方式——它通常会捕获明显的错误,但这不是必需的,由调用者确保他们没有做任何愚蠢的事情。

        【讨论】:

          【解决方案4】:

          您的x 变量不是指针,如果您想为它分配new Derived&lt;int&gt;,它应该是。

          至于删除堆栈上的东西,不要这样做。没有办法告诉您是否传递了堆栈或堆上某些东西的地址(实际上,C++ 标准甚至不承认堆栈的存在)。这里的教训是,你不应该删除不属于你的东西,尤其是当你无法知道它们来自哪里时。

          【讨论】:

          • 一方面你纠正了你的例子,另一方面它是正确的,因为你可以用第二个构造函数从指针初始化Derived&lt;int&gt;(或者,至少你打算能够)。
          • A Derived&lt;int&gt;* 可以作为构造函数参数。所以你的第一句话是不正确的......
          • 这可能是法律允许的,但几乎可以肯定这不是他想要做的,如果是这样的话,那将是非常糟糕的做法。我从未声称他的所作所为是非法的。
          【解决方案5】:

          不确定为什么要引用指针。为什么不

          Base(Base<T> * other) { }
          

          Derived(Derived<T>* other): Base<T>(other) {}
          

          应该可以的。

          而且,和其他回答一样,我认为您不能合法地知道指针是否指向堆。

          编辑:为什么不能做你想做的事:考虑例子:

          Derived1<int> *x = new Derived1<int>
          Base<int> **xx =&x;
          Derived2<int> y;
          *xx = &y;
          

          Derived1 和 Derived2 是从 Base 派生的不同类?你觉得合法吗?现在 Derived1* 类型的 x 指向 Derived2?

          【讨论】:

          • 我确实想要引用指针。在实际代码中,我需要访问和更改 Derived 的成员。
          • 但是如果你有指针,你已经可以改变 Derived 的成员了
          • 您需要引用指针才能更改指针本身。但这应该是不可能的,因为它允许您将 Derived* 设置为某个 Base*,而不是 Derived*。
          • 罗杰,第二个想法对我来说也没有多大意义。
          • 我不清楚。我想将“其他”更改为指向其他内容或设置为 NULL。如果我没有参考资料,我将无法这样做,因为我只会使用副本。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-18
          • 2021-07-11
          • 2012-10-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多