【问题标题】:C++ pointer argument getting set to NULL just before 'ret' instruction [duplicate]C ++指针参数在'ret'指令之前设置为NULL [重复]
【发布时间】:2014-07-30 17:51:53
【问题描述】:

我遇到了一个特殊的问题。我以为我知道相当多的 C++,但我一定忽略了一些细微差别?

我有一些模板类,基本上所有发生的事情都是基于某种条件,一个对象正在从一个 std::list 移动到另一个。

这里关注的方法是doAndGet(T *t)。我的意图是让 t 以NULL 的形式出现(或者其他什么,我真的不在乎,但它应该是NULLnullptr,如果我最终将T 元素*tmyListmyOtherList,我希望 T *t 指向被移动的项目。

现在可能有一些关于std::list 的东西我不知道,但是当单步执行我的代码时,t 被正确分配并且——最初我认为在方法返回之后,但后来发现——对在ret 指令之前,参数T *t 被设置回NULL

任何大师都可以阐明这个问题吗?真是让我头疼……

template<typename T, typename C>
class Base : IsDerivedFrom<T, C>
{
public:
    typedef std::list<T*> SpecialList;
    virtual bool doAndGet( T* t ) = 0;

protected:

    SpecialList myList;
};

template<typename T>
class Derived : public Base<T, Other>, public Other
{
public:
    Derived( void );
    virtual ~Derived( void );

    bool doAndGet( T* t );

protected:

    typename Base<T, Other>::SpecialList myOtherList;

};

template<typename T>
bool Derived<T>::doAndGet( T *t )
{
    // Nothing to process
    if (myOtherList.empty()) {return false;}

    t = (*myOtherList.begin());
    myOtherList.pop_front();

    if (true/* some condition needs to be moved */)
    {
        t->status = 1;
        this->myList.push_back(t);
        return true;
    } else
    {
        // Something bad happened...
        delete t;
        t = nullptr;
        return false;
    }
}

// Use... (pretend I defined MyOtherClass somewhere, not important)
Derived<MyOtherClass> derivedInstance;

MyOtherClass *t = nullptr;
if ( derivedInstance.doAndGet(t) )
{
    // ... Here I should expect `t` to be not `NULL` and not `nullptr`
    // This however, is not ever the case in my testing...
}

【问题讨论】:

  • doAndGet 无法修改传入的t,因为它是按值传递的。如果您在doAndGet 中分配t 应该有副作用,请通过引用获取t(例如T*&)。
  • 您的基类缺少虚拟析构函数(但这不是问题)。
  • 你是如何在不了解 pass-by-value 的情况下学习模板、多重继承、奇怪重复的模板模式的 :)

标签: c++ templates pointers arguments


【解决方案1】:

如果您想将一个变量传递给一个函数,并且将该函数中的任何更改反映给调用者,您需要使用一个引用。

C++ 和 C 一样,是按值传递的,其中函数只获取变量的 副本。 C++ 有引用允许通过引用传递。

换句话说,你的函数签名应该是:

bool doAndGet( T* &t );

声明t 是对T* 的引用,允许更改反映回调用者中的原始指针。

一个更简单的例子如下:

#include <iostream>
static void change (int a, int &b) { a = b = 0; }
int main() {
    int a = 42, b = 42;
    change (a, b);
    std::cout << a << ' ' << b << '\n';
    return 0;
}

这会输出42 0,因为传入的a 是按值传递的,并且更改不会反映给调用者。 b 作为引用传入,因此更改反射回来。

【讨论】:

  • 我正在传递一个指针,因为我没有什么可以给函数,如果它返回 true,我希望指针指向一个有效的对象。
  • 轰隆隆。你搞定了。谢谢!
  • @Volte,没错,但 pointer 不能更改,除非您有参考。指针指向的东西是可以改变的(这就是缺乏引用的 C 确实通过引用传递的方式)但是你的函数想要改变实际的指针。
  • 我还有一个半相关的问题。在我在其他地方的代码中,我有另一个类似的方法,它有 read( T *t, SomeClass &amp;s ) 传递给我,就像我在原始问题中一样,T *p = NULL 并在返回时正确设置 p... 为什么?这在某些情况下有效吗?
  • 实际上,我收回了这一点。我认为有效的实际上不是......我用我新发现的知识更新了那些旧功能(感谢你们),这一切都非常好!
【解决方案2】:

doAndGet 按值传递其参数。所以调用函数中的参数没有改变。

同一事物的更简单示例:

void func(int x)
{
    ++x;
}

int main()
{
    int y = 5;
    func(y);
    std::cout << y << "\n";   // outputs 5
}

【讨论】:

  • 但是我传递了一个指针...
  • 您正在按值传递指针。函数中的t 和调用者中的t 是单独的变量。 (两个指针指向同一个东西,但指针本身位于不同的位置)
  • 所以我应该将指针传递给指针...?
  • @Volte 是的,但是指针本身是按值传递的。您可以修改所指向的项目,但不能重新定位指针并使该更改在函数外部可见。将参数更改为 T *&amp; t 以使其按预期工作。
  • 是的,或者在 C++ 中你可以通过引用传递,例如bool Derived&lt;T&gt;::doAndGet( T * &amp;t )
猜你喜欢
  • 1970-01-01
  • 2016-05-24
  • 1970-01-01
  • 1970-01-01
  • 2014-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-01
相关资源
最近更新 更多