多次调用构造函数会改变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-&gt;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++ 中的指针成员初始化对象

复制构造函数中的c ++用户定义成员

C++中的派生类,可以不定义对象直接调用基类的成员和调用自己的成员函数嘛???

C++构造函数的初始化列表

C++类和对象(this指针6个默认成员函数const成员)

07. this指针,构造和析构,new和delete