多次调用构造函数会改变C++中的成员指针地址
Posted
技术标签:
【中文标题】多次调用构造函数会改变C++中的成员指针地址【英文标题】:Multiple calling constructor will change the member pointer address in C++ 【发布时间】:2019-03-08 05:38:08 【问题描述】:我写了一个程序检查类创建的init过程,发现多次调用构造函数改变了成员指针地址。看看下面的sn-p。
#include <iostream>
using namespace std;
class FF
public:
FF()
this->ptr = NULL;
value = 1;
cout << "ptr address in 1: " << this->ptr <<endl;
FF(const int* ptrcopy, const int valuecopy)
cout << "ptr address in 2: " << this->ptr << endl;
FF();
/* Is this equivalent with FF() ?
this->ptr = NULL;
value = 1;
*/
init(ptrcopy, valuecopy);
void init(const int* ptrcopy, const int valuecopy)
cout << "ptr address in 3: " << this->ptr << endl;
if (this->ptr != NULL)
cout << "error happened, the address of ptr is " << this->ptr << endl;
return;
private:
int* ptr;
int value;
;
int main()
int *ptr = new int(10);
int value = 1;
FF fclass(ptr, value);
delete(ptr);
return 0;
输出是
ptr address in 2: 0x400b40
ptr address in 1: 0
ptr address in 3: 0x400b40
error happened, the address of ptr is 0x400b40
看来FF()
的调用只是将ptr所在空间初始化为NULL,调用后ptr又变回原来的0x400b40。
有人可以解释一下吗?
【问题讨论】:
离题:更喜欢 C++ 关键字 (nullptr
) 而不是旧的(过时的)C 宏 (NULL
)。优先使用构造函数的初始化列表(不要与std::initializer_list
混淆)而不是in-body-initialisation,即。 e. FF() : ptr(nullptr)
而不是 FF() ptr = nullptr;
;您避免默认初始化+赋值,而是直接使用参数进行初始化,在某些情况下(引用、没有默认构造函数的类、const 对象),初始化列表是构造/初始化成员的唯一方法。
【参考方案1】:
您对FF();
的调用将创建一个基于FF
的新的未命名堆栈对象,构造它(生成您看到的输出),然后立即再次销毁它(您不会显示任何输出)。这就是ptr
地址似乎变回来的原因——因为它从未改变。添加一个析构函数,打印出this
的地址以查看这种情况。
顺便说一句,您在第二个(参数化)构造函数中使用 this->ptr
是未定义行为,因为您从未为 ptr
赋值。
如果您的意图是从参数化构造函数中调用默认构造函数,并且您的编译器支持 C++11,则可以delegate 到默认构造函数。
FF(const int* ptrcopy, const int valuecopy): FF() /* ... */
【讨论】:
值得一提的构造函数委托 (FF(int const*, int) : FF() /*...*/
)?在我看来,这就是她/他实际上试图做的事情......【参考方案2】:
我认为正在发生的事情是,在构造函数 2 中,您正在打印 ptr (0x400b40
) 的未初始化值,然后您正在使用 FF()
创建一个 FF 类型的新对象。然后将为新对象调用构造函数 1,它的 ptr 成员将更改为 NULL(因此打印时它将为 0)。新对象的构造函数完成后,它返回到构造函数 2(调用先前对象的析构函数),然后调用 init,它将显示与以前相同的 ptr 值,因为该对象的 ptr 成员尚未更改。
它也可以帮助你在析构函数中打印一些东西。那将是FF::~FF()
。
编辑:拼写和析构函数建议
【讨论】:
【参考方案3】:就像 1201ProgramAlarm 说的那样,做FF();
不会不调用 current 对象的构造函数。为此,您需要执行以下操作(假设 C++11):
class FF
public:
FF() : ptr(nullptr), value(1)
cout << "ptr address in 1: " << this->ptr <<endl;
FF(const int* ptrcopy, const int valuecopy) : FF()
cout << "ptr address in 2: " << this->ptr << endl;
init(ptrcopy, valuecopy);
void init(const int* ptrcopy, const int valuecopy)
...
...
;
See also this question.
【讨论】:
【参考方案4】:比较以下:
class C
public:
C() std::cout << 'c'
~C() std::cout << 'd'
;
void test()
C f;
std::cout << 't';
您应该已经知道,您在函数结束时创建了一个超出范围的临时对象。你应该看到输出ctd
void test()
C();
std::cout << 't';
同样,有一点不同:对象在语句执行后立即超出范围,因此输出将是cdt
。
现在在您的构造函数中发生的情况完全相同,您只需在构造函数的主体内调用 FF()
时创建一个临时的、单独的 FF 对象。
我想你打算改为构造函数委托(自 C++11 起可用);但是,语法不同:
FF(int const*, int)
: FF() // as if using the initialiser list
/* can do some extra work here */
现在,在进入函数体之前,将在this
上调用默认构造函数。当然,只要调用得当,就可以使用 any 构造函数:
FF() : FF(nullptr, 0)
现在,默认构造函数将调用您的第二个构造函数。
【讨论】:
以上是关于多次调用构造函数会改变C++中的成员指针地址的主要内容,如果未能解决你的问题,请参考以下文章
C++中的派生类,可以不定义对象直接调用基类的成员和调用自己的成员函数嘛???