当被称为父类的指针时,对象丢失其成员的数据

时间:2017-08-04 23:51:05

标签: c++ c++11 lifetime

此计划的输出缺少for(var i = 0; i < 10; i++){ add({id: `${i}_idstring`}); } name的预期值。

  

姓名:,年龄:4岁,有些字符串:

     

姓名:,年龄:3,有些字符串:

如果我为Some stringmyVector的每个项目动态分配内存,那么它可以正常运行。我只是想知道为什么会这样做,以及我犯了什么错误。

值得注意的是我使用的是Visual Studio 2015 C ++编译器。

Parent.h

Source.cpp

Child.h

#pragma once

#include <string>

class Parent
{
public:
    explicit Parent(std::string name, int age)
        : m_name(name), m_age(age) {};

    virtual const std::string toString() const
    {
        return "Name: " + m_name + ", Age: " + std::to_string(m_age);
    }

private:
    std::string m_name;
    int m_age;
};

Source.cpp

#pragma once

#include "Parent.h"

class Child : public Parent
{
public:
    explicit Child(std::string name, int age, std::string someString)
        : Parent(name, age), m_someString(someString) {};

    virtual const std::string toString() const
    {
        return Parent::toString() + ", Some string: " + m_someString;
    }
private:
    std::string m_someString;
};

2 个答案:

答案 0 :(得分:3)

写作时

myVector.push_back(&Child("Foo", 4, "Test"));

创建一个Child类型的临时对象,并将其指针保存在向量中。

但它是一个临时对象,在push_back()结束时立即被销毁;所以在向量中记忆的指针的值指向一个可用且可能被回收的内存区域。

使用此指针时

std::cout << p->toString() << std::endl;

行为未定义。

在分配对象的内存时有所不同,因为在push_back()调用后对象没有被立即销毁,并且当您调用toString()时对象仍然可用[但记得要打电话delete,最后,或(更好)使用智能指针]。

- 编辑 -

你要求

  

注意添加智能指针如何适应这个问题

所以我使用std::unique_ptr显示了一个简单的简化示例。

请注意,您必须使用emplace_back()代替push_back();这是因为std::unique_ptr不是复制构造函数(出于明显的原因)[无论如何,我建议尽可能使用emplace_back(),而不是push_back()]。

#include <vector>
#include <memory>
#include <iostream>

struct foo
 { std::string str; };

int main ()
 {
   std::vector<std::unique_ptr<foo>> myV;

   myV.emplace_back(new foo{"one"});
   myV.emplace_back(new foo{"two"});

   for ( auto const & p : myV )
      std::cout << p->str << std::endl;

   return 0;
 }

- 编辑2 -

正如Daniel Schepler指出的那样(谢谢!),在std::vector中添加值

myV.emplace_back(new foo{"one"});
如果myV中的新元素导致向量的内部存储器的新分配并且此分配失败,则

会生成内存泄漏。

这是一个不太可能的案例,但它是可能的,所以我认为避免风险更好(妄想是一种资产)。

解决此问题的方法是在reserve()之前调用emplace_back(),因此无需在new

之后重新分配内部内存
myV.reserve(2U); // or more
myV.emplace_back(new foo{"one"});
myV.emplace_back(new foo{"two"});

但是,为了解决这类问题,C ++ 14引入了std::make_unique,所以在C ++ 14中可以一起使用push_back()(但这个例子需要一个foo(std::string const &)构造函数对于结构foo

myV.push_back(std::make_unique<foo>("one"));
myV.push_back(std::make_unique<foo>("two"));

另一种方式,也适用于C ++ 11,正在myV临时std::unique_ptr

myV.push_back(std::unique_ptr<foo>{ new foo{"one"} });
myV.push_back(std::unique_ptr<foo>{ new foo{"two"} });

因此可以称为std::unique_ptr的移动构造函数,并且在重新分配向量失败的情况下,临时std::unique_ptr的析构函数释放已分配的内存。

答案 1 :(得分:2)

使用std::vector<Parent*> myVector时;指针的向量,你必须为对象分配内存:

   myVector.push_back( new Child("Foo", 4, "Test"));

为避免动态创建对象,请使用:

std::vector<Parent> myVector

   myVector.push_back(Child("Foo", 4, "Test"));

由于您正在询问如何摆脱已分配的对象,因此这是一个建议:

clearAndDestroy(&myVector);

功能是:

template <typename V>
void clearAndDestroy( std::vector<V*> *&myV)
{
    if(myV == NULL)
        return;

    std::set<V*> mySet;

    typename std::vector<V*>::iterator itr;
    typename std::set<V*>::iterator sitr;

    itr = myV->begin();
    while (itr != myV->end())
    {
        mySet.insert(*itr);
        ++itr;
    }

    sitr = mySet.begin();
    while (sitr != mySet.end())
    {
        delete(*sitr);
        ++sitr;
    }

    myV->clear(); // Removes all elements from the vector leaving the container with a size of 0.
}