A vector member is reset and unaccessible

2019-08-30 01:46发布

问题:

I have two projects, one basic client and a dynamic library. Here's what happens in the client:

int main()
{
    Scrutinizer scru;
    scru.Scrutinize();
    return 0;
}

In the DLL, The Scrutinizer class is as such (__declspec(dllexport) and such omitted for Clarity)

Header

class ProcessesGenerator;

    class Scrutinizer
    {
    public:
    Scrutinizer();
    ~Scrutinizer();
    ProcessesGenerator *ProcGenerator
    void Scrutinize();
};

The forward declaration of ProcessesGenerator was 'mandatory' for me to avoid some kind of circular reference.

Constructor in .cpp file

Here is how I initialize it:

Scrutinizer::Scrutinizer()
{
    ProcGenerator = &ProcessesGenerator();
}

More about this ProcessesGenerator class:

Header

class ProcessesGenerator
{

public:
    ProcessesGenerator();
    ~ProcessesGenerator();
    WinFinder winFinder;
    std::vector<std::string> fooCollec;

    void GenerateProcesses();
};

ProcessesGenerator.cpp

Constructor:

ProcessesGenerator::ProcessesGenerator()
{
    //winFinder = WinFinder();//problem will be the same with or without this line
    fooCollec = std::vector<std::string>{"one", "two", "three"};
}

A breakpoint in the constructor shows that the vector is initialized with the chosen values.

Problematic function:

void ProcessesGenerator::GenerateProcesses() {
    std::string foo = "bar";
    fooCollec = std::vector<std::string>{};//read access violation
    fooCollec.push_back(foo);//read access violation
    winFinder.SomeVector= std::vector<std::string>{};//read access violation
}

Once there, I Can see that the size of vector is reset to 0. Any attempt to re-initialize it, or to push an element results in read access violation .Same with the vecotr member of its WinFinder member. I guess the flaw is obvious, but I really don't get it,

Thanks!

回答1:

Your problem is with

Scrutinizer::Scrutinizer()
{
    ProcGenerator = &ProcessesGenerator();
}

What you are doing is taking the address of a temporary object. That object will be destroyed and the end of that line and you will be left with a pointer that doesn't point to a valid object.

The old way to fix it would be to use

Scrutinizer::Scrutinizer()
{
    ProcGenerator = new ProcessesGenerator();
}

But now you have to implement the copy constructor, copy assignment operator, and the destructor. Since you have a modern compiler what you can do instead is make ProcGenerator a std:unique_ptr<ProcessesGenerator> and then Scrutinizer() becomes

Scrutinizer::Scrutinizer() : ProcGenerator(make_unique<ProcessesGenerator>()) {}

I would also like to add that &ProcessesGenerator(); should not even compile. Unfortunately MSVS has a non-standard extension that allows this to compile. You can turn on the /Za compiler option (enforce ANSI compatibility) and then you should get an error like

error C2102: '&' requires l-value



回答2:

The line ProcGenerator = &ProcessesGenerator(); makes a temporary ProcessesGenerator, takes its address and then puts it in your ProcGenerator pointer. The temporary is then destroyed, leaving garbage.

You probably wanted to be allocating it on the heap ProcGenerator = new ProcessesGenerator; but even in that case I would strongly suggest using unique_ptr instead of a raw pointer.