我有一节课,我们称之为A
。类A
有两个子类,a
和b
。
我正在制作一个类A
的指针:
A *pointer;
在程序中的某个时刻,我像这样初始化指针:
pointer = new a();
在其他方面,我运行了类A
的函数:
pointer->function(&pointer);
此函数在类A
内(因此所有子类都有它)。有可能在调用此函数时,我想更改指向另一个子类的指针,这是我尝试过的:
void A::function(A **pointer)
{
if (something)
{
delete *pointer;
*pointer = new b();
}
}
虽然这很有效,但我真的很好奇,如果这是一个好习惯,我从对象内部调用delete
并释放对象本身,这可能是未定义的行为,我很幸运它有效吗?我不理解这个吗?我是否比这更复杂?
答案 0 :(得分:3)
是的,只要你小心,这是有效的。请参阅a question specifically about delete this
上的更多讨论。
但是,与C ++中的其他内容一样,只要您小心,它们就是有效的,您最好找到另一种不易出错的解决方案。我建议你将代码重新编写成一个函数返回一个新指针,然后自动销毁旧指针(例如通过智能指针)。
有些事情:
struct A {
static std::shared_ptr<A> function(std::shared_ptr<A>& ptr, int x) {
if (x > 0)
return std::make_shared<A>(x);
else return ptr;
}
A(int _x): x(_x) {}
int x;
};
另请注意,我将function()
设为static
,因为它无论如何都接受该对象作为其第一个参数。请参阅live on coliru。
事实上,我不太喜欢shared_ptr
这个解决方案,如果有人能更好地实现这种方法,我会很高兴知道。
答案 1 :(得分:1)
此代码有效(有关正确性的详细信息,请参阅this answer)
但这不是一个好习惯,因为其他开发人员可能会错过细微差别,使用其中一个成员函数会导致重建对象。
明确重建对象比隐藏成员函数更好。
或者只使用智能指针。
答案 2 :(得分:1)
作为一种设计我不喜欢指针突然指向另一个对象(不同类型),当它不清楚它发生时。可以说,由于OPs代码通过&pointer
,它表明它可能会发生变化。但是,我更喜欢作业 - 我认为这更清楚。
我会尝试这样的事情:
int uglyGlobal = 1; // don't try this at home... ;-)
class A
{
public:
int n;
A() {n = uglyGlobal++; cout << "A cons for #" << n << endl;}
virtual ~A() {cout << "A des for #" << n << endl;}
unique_ptr<A> function(int something, unique_ptr<A> ptr);
};
class a : public A
{
public:
a() {cout << "a cons" << endl;}
~a() override {cout << "a des" << endl;}
};
class b : public A
{
public:
b() {cout << "b cons" << endl;}
~b() override {cout << "b des" << endl;}
};
unique_ptr<A> A::function(int something, unique_ptr<A> ptr)
{
if (something == 0)
{
// Turn it into an A
return unique_ptr<A>(new A);
}
else if (something == 1)
{
// Turn it into an a
return unique_ptr<A>(new a);
}
else if (something == 2)
{
// Turn it into an b
return unique_ptr<A>(new b);
}
else
// Keep the current
return ptr;
}
int main()
{
cout << "Make A" << endl;
unique_ptr<A> x (new A);
cout << "1. call - turn A into a" << endl;
x = x->function(1, move(x));
cout << "2. call - turn a into b" << endl;
x = x->function(2, move(x));
cout << "3. call - turn b into another b" << endl;
x = x->function(2, move(x));
cout << "4. call - keep current b" << endl;
x = x->function(3, move(x));
cout << "Return from main" << endl;
return 0;
}
输出结果为:
Make A
A cons for #1
1. call - turn A into a
A cons for #2
a cons
A des for #1
2. call - turn a into b
A cons for #3
b cons
a des
A des for #2
3. call - turn b into another b
A cons for #4
b cons
b des
A des for #3
4. call - keep current b
Return from main
b des
A des for #4
答案 3 :(得分:0)
通常,您必须确保调用function
的每个执行路径都没有针对A或派生类(this
的方法的堆栈帧无效)。
所以,这很危险。在MFC api编程中,这种情况发生在#34;通常&#34;在WM_NCDESTROY消息处理程序中。在其中您执行delete this
之类的操作,但Windows确保WM_NCDESTROY是发送到窗口的最后一条消息。
我建议你改变一下A类的api并使用unique_ptr来处理内存:
#include <memory>
class A
{
public:
std::unique_ptr<A> f()
{
return std::make_unique<A>();
}
};
int main()
{
auto p = std::make_unique<A>();
p = std::move(p->f());
return 0;
}
通过这种方式,您可以将销毁从f()
内部移动到p。