C ++指针与类成员变量的混淆

时间:2019-06-16 19:27:04

标签: c++

我想知道类成员(属性)的生命周期是什么。

当前我有这个课程:

class Snake {

private:
    std::vector<Position> positions = std::vector<Position>();

public:
    void addNode(Position * position);
};

并且不确定addNode函数是否必须接收指针或对象? 我的猜测是,如果addNode接收到Position位置作为参数,则对象将超出其创建范围,例如

... code here ...
{
Position p = Position(..);
snake.addNode(p);
}
// p should die over here so it could cause a null pointer on the program

另一方面,如果addNode收到一个指针,则p除非我删除它,否则它不会死亡(因此它不会导致任何空指针),也许你们可以帮我澄清一下有关我现在拥有的指针的巨大困惑,

谢谢。

4 个答案:

答案 0 :(得分:2)

对象不必是指针,但是指针是对象。指针只是一个存储数字的对象,该数字是某个对象的内存地址。当指针超出范围时,它不会删除其指向的对象(因此会导致内存泄漏)。

如果您希望自己的代码性能最高并且更正,则应该:

  1. 使用const引用代替指针。
  2. 提供功能的移动版本。

如下所示,这将是代码:

void addNode(const Position& position)
{
    positions.push_back(position);
}
void addNode(Position&& position)
{
    positions.push_back(std::move(position));
}

它可以避免制作冗余副本并使用const Position作为参数。 这种方法比其他answears(void addNode(Position position))中建议的方法要快,因为它避免了将参数复制到函数中。

另一种优化方法是,当您知道该位置将超出范围时,将位置移至该函数:

{
    Position p = Position(x, y);
    snake.addNode(std::move(p)); //mind the std::move
} //p will go out of scope here anyway, so it can safely be moved

答案 1 :(得分:1)

我们假设addNode已实现

addNode(Position p){
    positions.push_back(p);
}

输入变量是您传入的变量的副本。在向量上调用push_back后,向量内将创建另一个副本,当向量被销毁时,该副本也将被销毁。此后p将被销毁,但这没有影响,因为vector包含一个副本。通常,如果编译器很聪明,则可以避免某些复制,并且可以通过使用例如std :: move来改进代码,但我不想在答案中增加太多复杂性。

您对“空指针”的评论有点不准确:如果您有一个指针向量,则可以在向量中插入一个指向p的指针。如果p超出范围,指针将悬空,从而使程序的行为不确定。

答案 2 :(得分:0)

不要使用指针,这明显是怎么回事?

public:
    void addNode(Position position);

关键是尽管函数退出后position参数就会消失,但它的值是向量中的 COPIED ,并且副本仍然存在于向量中,即使原始的已经死了。

这里真的不需要指针。

答案 3 :(得分:0)

类成员的生命周期与实例相关。它的生命始于构造函数的初始化程序列表,并终止于析构函数调用的末尾。

您似乎给人的印象是,类是参考值-使用C#术语-不是。所有C ++对象都是值类型:

  • {Position p;...}是类型Position的对象。它的构造函数在块的开头创建对象,并在结尾处调用其析构函数,这会破坏其内容和对象本身。
  • {Position* ptr{&p};...}是类型Position*的对象。它是“构造函数”来初始化指针。它的析构函数是no-op,但是如果存在,那么它将破坏它的value = address = number而不是它所指向的对象。指针是指向另一个对象的地址,并且在该块的末尾,无论指针的内容如何,​​该指针都会被破坏。就像{int i = 0;}会破坏i,而不是0

  • {Position& ptr{p};...}与指针的规则相同。引用被销毁,而不是被引用的对象。

核心思想是指针只是另一种普通的值类型:

Position p;
//Introduces a new name for a type
using Position_p = Position*;

Position_p p1 = &p;
Position_p p2 = p1;
p2++; // Change p2
//p1 is still the same.
int i1 = 5;
int i2 = i1;
i2++; // Change i2
//i1 is still the same.

它们看起来相同,甚至副本也表现出应有的状态。就像i2获取i1的内容一样,p2获取p1的内容。对于int,内容是数字;对于Position_p,内容是地址。如果您了解这个概念,那么指针应该变得非常直观。

所以我的猜测是您想拥有std::vector<Position>addNode(Position p)。但这会产生不必要的副本,因此addnode(const Position& p)更好。在内部可以执行push_back,并将值复制到向量中。如果需要,可以使用move语义摆脱最后的副本。