3

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

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;}

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

4

5 回答 5

10

Base<T>是 的基类型Derived<T>,但Base<T>*不是 的基类型Derived<T>*。您可以传递派生指针代替基指针,但不能传递派生指针引用代替基指针引用。

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

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

您刚刚将一个指向非,Derived<T>的指针写入指向Derived<T>. 编译器不能让这种情况发生。

于 2009-12-22T21:25:19.590 回答
3
  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上述相同的操作。

于 2009-12-22T21:16:19.953 回答
2

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

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

于 2009-12-22T21:08:06.727 回答
0

您的x变量不是指针,如果您想为它分配 a ,它应该是new Derived<int>

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

于 2009-12-22T21:08:31.843 回答
0

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

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?

于 2009-12-22T21:15:24.353 回答