-
21-09-2019 - |
题
我有以下人为的示例(来自真实代码):
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;}
如何确保指针合法地指向堆上的某些内容?
解决方案
Base<T>
是Derived<T>
的基类型,但Base<T>*
不是Derived<T>*
的基类型。可以传递派生的指针代替基指针的,但不能代替基指针参考的传递派生的指针参考。
的原因在于,假设你可以,并假设基地的构造是写一些值到参考:
Base(Base<T> * &other) {
Base<T> *thing = new Base<T>(12);
other = thing;
}
您刚刚写指针的东西是的不的一个Derived<T>
,成指针Derived<T>
。编译器不能让这种情况发生。
其他提示
- 不能将对 Derived 指针的引用转换为对 Base 指针的引用。(模板不会造成这里的问题,因此从下面的示例中删除。)
- 如果您想推迟对指针的责任,请使用智能指针类型。智能指针类型可以代表原始指针所不能的“删除责任”。示例包括 std::auto_ptr 和 升压::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
多于。
您确保指针指向写你的文档中,并依靠来电者通过遵守东西就堆。如果谁调用构造函数传递一个堆栈指针,全盘皆输,这不是你的错 - 你可以尝试及早发现问题,但不能保证
。这是如何的标准库的作品 - 往往会抓住明显的错误,但它并不需要,而且它是由主叫方,以确保他们没有做任何愚蠢的事
您x
变量不是指针,它应该是,如果你想一个new Derived<int>
分配给它。
至于在堆栈上删除的事,不这样做。有没有办法告诉你是否已通过堆栈或堆(事实上,C ++标准甚至不承认一个栈的存在)的东西的地址。这里的教训是,你不应该删除的东西,你不拥有,特别是如果你没有告诉他们来自何处的方式。
不知道你为什么要参考指针。为什么不
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的被不同类从碱衍生?你会认为这是合法的?现在的类型Derived1 *点是x到Derived2的?