简体   繁体   English

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

[英]Object loses data of its members when referred to as pointer to parent class

This program's output is missing the intended values for name and Some string . 该程序的输出缺少nameSome string的预期值。

Name: , Age: 4, Some string: 名称:,年龄:4,某些字符串:

Name: , Age: 3, Some string: 名称:,年龄:3,一些字符串:

If I dynamically allocate memory for each item for myVector in Source.cpp then it works out okay. 如果我动态为每个项目分配内存myVectorSource.cpp那么好的作品出来。 I'm just wondering why this works out as it does, and what mistakes I have made. 我只是想知道为什么会这样,以及我犯了什么错误。

Worth noting is I'm using Visual Studio 2015 C++ compiler. 值得注意的是我正在使用Visual Studio 2015 C ++编译器。

Parent.h Parent.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;
};

Child.h Child.h

#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;
};

Source.cpp Source.cpp

#include <vector>
#include <iostream>

#include "Parent.h"
#include "Child.h"

int main()
{
    std::vector<Parent*> myVector;

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

    for (auto p : myVector)
    {
        std::cout << p->toString() << std::endl;
    }

    return 0;
}

When you write 当你写

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

you create a temporary object of type Child and save its pointer in the vector. 您将创建一个类型为Child的临时对象,并将其指针保存在向量中。

But it's a temporary object that is immediately destroyed when push_back() ends; 但这是一个临时对象,当push_back()结束时会立即销毁; so the value of the pointer memorized in the vector point to an area of memory that is free and, probably, recycled. 因此,存储在向量中的指针值指向的内存区域是空闲的,并且有可能被回收了。

When you use this pointer 使用此指针时

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

the behavior is undefined. 行为是不确定的。

Is different when you allocate the memory of the object because the object isn't destroyed immediately after the push_back() call and the object is still available when you call toString() [but remember to call delete , at the end, or (better) use smart pointers]. 分配对象的内存时有所不同,因为在push_back()调用之后该对象并没有立即被销毁,并且在调用toString()时该对象仍然可用[但是请记住在最后调用delete ,或者(更好)使用智能指针]。

-- EDIT -- -编辑-

You ask for 你要

care to add how smart pointers fit in this problem 小心添加智能指针如何解决此问题

so I show a simple simplified example using std::unique_ptr . 因此,我展示了一个使用std::unique_ptr的简单示例。

Observe that you have to use emplace_back() instead of push_back() ; 注意您必须使用emplace_back()而不是push_back() this is because the isn't a copy constructor (for evident reasons) for std::unique_ptr [anyway, I suggest to use emplace_back() , when possible, instead 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;
 }

-- EDIT 2 -- -编辑2-

As pointed by Daniel Schepler (thanks!), adding values in the std::vector with 正如Daniel Schepler所指出的(谢谢!),在std::vector添加以下值std::vector

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

can generate a memory leak in case the new element in myV cause a new allocation of the internal memory of the vector and this allocation fail. 万一myV的新元素导致向量内部存储器的新分配而该分配失败,则可能会产生内存泄漏。

It's an unlikely case but it's possible so I think it's better to avoid the risk (the paranoia is an asset). 这种情况不太可能发生,但是有可能,所以我认为最好避免这种风险(妄想症是一种资产)。

A way to solve this problem is call reserve() before emplace_back() , so there is no need to reallocate the internal memory after the new 解决此问题的一种方法是在emplace_back() reserve()之前调用reserve() ,因此无需在new内存之后重新分配内部内存。

myV.reserve(2U); // or more
myV.emplace_back(new foo{"one"});
myV.emplace_back(new foo{"two"});

but, to solve this sort of problem, C++14 introduced std::make_unique , so in C++14 can be used together push_back() (but this example require a foo(std::string const &) constructor for the struct foo ) 但是,为解决此类问题,C ++ 14引入了std::make_unique ,因此在C ++ 14中可以将push_back()一起使用(但此示例要求foo(std::string const &)构造函数用于struct foo

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

Another way, also for C++11, is moving in myV a temporary std::unique_ptr 同样对于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"} });

so can be called the move constructor of std::unique_ptr and, in case of failure reallocating the vector, the destructor of the temporary std::unique_ptr free the allocated memory. 因此可以称为std::unique_ptr的move构造函数,并且在重新分配向量失败的情况下,临时std::unique_ptr的析构函数将释放已分配的内存。

When you use std::vector<Parent*> myVector ; 当您使用std::vector<Parent*> myVector vector of pointers you have to allocate memory for the object: 您必须为对象分配内存的指针向量:

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

To avoid dynamic creation of objects use: 为避免动态创建对象,请使用:

std::vector<Parent> myVector

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

Since you are asking how to get rid of the allocated objects this is a suggestion below: 由于您正在询问如何摆脱分配的对象,因此以下建议:

clearAndDestroy(&myVector);

And the function is: 函数是:

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.
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM