开发者

Do containers (sets, vectors, etc) need to be created using the new keyword in order persist across functions?

I've been working on a school assignment that makes pretty heavy use of vectors, sets, stacks and queues.

What is the difference between Foo and Bar, especially when passing Foo and Bar between functions? When would it be safe to call delete on Bar, if ever? I'm guess it's never safe to call delete on Bar unless everything in that vector has been moved? If I return Foo, wont it (and its contents) be deleted when the function exits?

vector<Line *> Foo;

and:

vector<Line *> * Bar = new vector<Line *>();

Similary, lets say I have a function:

vector<Line *> BSTIndex::query(string expression)
{
    vector<Line *> result; //Holds the lines that match the expression
    string query = expression;
    queue<string> * output = expressionParser(query);
    doSomeStuffWithOutputHere();
    return result;
}

And my expressionParser is:

queue<string> * BSTIndex::expressionParser(string query)
{开发者_JAVA技巧
    char * cQuery = new char[100];
    strcpy_s(cQuery, 100,query.c_str());
    //strcpy(cQuery, query.c_str());
    queue<string> * output = new queue<string>(); //Operators go in the queue
    stack<string> * s = new stack<string>();  //Operands go on the stack
    performSomeMagicOnQueueAndStackHere();
    return output;
}

The stack is actually only local to expressionParser so I KNOW I can remove the new keyword from that. The queue however, needs to go back to the query function where it's used but then that's it. Do I HAVE To create a pointer to the queue in this case (I want to say yes because it's going to fall out of scope when expressionParser returns). If I need to create the pointer, then I should call a delete output in my query function to properly get rid of the queue?

My last concern is the vector being returned by query. Should that be left as I have it or should it be a pointer and what's the difference between them? Once I consume that vector (display the information in it) I need it to be removed from memory, however, the pointers contained in the vector are still good and the items being pointed to by them should not be deleted. What's the best approach in this case?

What happens to the contents of containers when you call delete on the container? If the container held pointers, and those pointers are deleted won't that affect other areas of my program?


SO many questions (direct and indirect) crammed into such a small space!

What is the difference between Foo and Bar.

vector<Line*>    Foo;
vector<Line*>*   Bar = new vector<Line*>();

Foo is an object of automatic (potentially static (but the distinction is not important here)) storage duration. This means it is created at the point of declaration (constructors called) and destroyed when it goes out of scope (destructors called).

Bar on the other hand is a pointer. Pointer have no constructors or destructors and are used to point at other objects (or NULL). Here Bar is initialized to point at an object of dynamic storage duration. This is an object that has to be manually released (otherwise it will leak).

especially when passing Foo and Bar between functions?

When passing Foo (by value) to/from functions the copy constructor is used to make a copy of the original object. Note: The standard explicitly states that copying from a function (via return) can be elided (look up RVO/NRVO) and all modern compilers will remove the extra copy construction and build in place at the return site (but this is an optimization invisible to the user just think of it as a very efficient copy out of a function). The resut of this is that when passed into a function (by value) or returned from a function (by return) you are working on a new object (not the original). This is important because of the side effects of using the object will not affect the original. (see your question below about returning Foo).

When passing Bar (by value) the pointer is copied. But this means that what is pointed at is the same. So modifying an object via the pointer is modifying the original value. This makes passing it as a parameter very cheap (because all you are passing is the address of the object). But it makes returning a value potentially dangerous, this is because you could return the address of an object that has gone out of scope inside the function.

Using pointers in inherently dangerous and modern C++ programs rarely use RAW pointers directly. Pointers are usually wrapped up inside objects that manage the ownership and potentially lifespan of the object (see smart pointers and containers).

When would it be safe to call delete on Bar, if ever?

It is only safe to delete Bar if:

  • It is NULL
  • The object it points at has been dynamically allocated via new.

I'm guess it's never safe to call delete on Bar unless everything in that vector has been moved?

It would safe to delete Bar even if it's content had not been moved (though you could leak if the contained pointers were owned by Bar (this is not directly unsafe but can be inconvenient when you run out of space)). This is another reason pointers are rarely used directly, there are no ownership semantics associated with a pointer. This means we can not tell if Bar owns the pointers it holds (it is the responsibility of the owner to call delete on a pointer when it is no longer used).

If I return Foo, wont it (and its contents) be deleted when the function exits?

Yes. But because you return an object it will be copied out of the function. So what you use outside the function is a copy of Foo (Though optimizers may elide the copy because of RVO/NRVO. Not if the copy is elided the destructor is also elided).

Similary, lets say I have a function: query and expressionParser:

vector<Line *> BSTIndex::query(string expression)
{
    vector<Line *> result; //Holds the lines that match the expression
    string query = expression;
    queue<string> * output = expressionParser(query);
    doSomeStuffWithOutputHere();
    return result;
}

queue<string> * BSTIndex::expressionParser(string query)
{
    char*    cQuery = new char[100];
    strcpy_s(cQuery, 100,query.c_str());
    queue<string> * output = new queue<string>(); //Operators go in the queue
    stack<string> * s = new stack<string>();  //Operands go on the stack
    performSomeMagicOnQueueAndStackHere();
    return output;
}

The stack is actually only local to expressionParser so I KNOW I can remove the new keyword from that.

Not only can you but you should. As currently you are leaking this object when it goes out of scope.

The queue however, needs to go back to the query function where it's used but then that's it. Do I HAVE To create a pointer to the queue in this case (I want to say yes because it's going to fall out of scope when expressionParser returns).

No. You can pass the obejct back by value. It will be correctly copied out of the function. If you find that this is expensive (unlikely) then you could create a dynamic object with new and pass it back as a pointer (but you may want to look at smart pointers). Remember that pointers do not indicate that you are the owner so it is unclear who should delete a pointer (or even if it should be deleted). So (if passing a value is too expensive) use a std::auto_ptr<> and pass the pointer back inside it.

If I need to create the pointer, then I should call a delete output in my query function to properly get rid of the queue?

Yes. And No. If you create a dynamic object with new. Then somebody must call delete on it. It is bad C++ style to do this manually. Learn to use smart pointers so that it is done automatically and in an exception safe manor.

My last concern is the vector being returned by query. Should that be left as I have it or should it be a pointer and what's the difference between them?

I would do it like this:

vector<Line> BSTIndex::query(string const& expression)
{
    vector<Line>    result;       // 1 Keep line objects not pointers.
    queue<string>   output  = expressionParser(expression);

    doSomeStuffWithOutputHere();

    return result;
}

queue<string> BSTIndex::expressionParser(string const& query)
{
    char    cQuery[100];               // Don't dynamically allocate
                                       // unless somebody takes ownership.
                                       // It would probably be better to use string or vector
                                       // depending on what you want to do.

    strcpy_s(cQuery, 100, query.c_str()); // std::string can use the assignment operator.

    queue<string>   output;            // Just build the que and use it.
    stack<string>   s;                 // Unless there is a need. Just use a local one.

    performSomeMagicOnQueueAndStackHere();

    return output;
}

Once I consume that vector (display the information in it) I need it to be removed from memory, however, the pointers contained in the vector are still good and the items being pointed to by them should not be deleted. What's the best approach in this case?

Depends who owns the pointers.

What happens to the contents of containers when you call delete on the container?

If the content of the containers are pointers. Then nothing. The pointers disappear (what they point at is unchanged and unaffected). If the container contains objects (not pointers) then the destructor is called on the objects.

If the container held pointers, and those pointers are deleted won't that affect other areas of my program?

It would. But you would have to call delete manually.


For the most part, what you normally want to do is just create an instance of the object in question, and when you need to return it, return it.

For something like a large container, that can (and often does) sound like it'd be horribly inefficient. In reality, however, it's usually quite efficient. The C++ standard has a couple of clauses to allow what are called Return Value Optimization (RVO) and Named Return Value Optimization (NRVO). Initially, what's in the standard doesn't sound very important -- it just says the compiler doesn't have to call the copy constructor for a returned value, even if the copy constructor has side effects so you can see that it's been omitted.

What this means in reality, however, is that the compiler (which is to say essentially all reasonably current compilers) will actually only create one collection at the highest level in the function call hierarchy where it gets used. When you end up returning the object of that type from some other function, instead of creating a local copy, and then copying it for the return, it'll just generate code in the called function that works directly in the collection in the calling function, so the return basically becomes a nop. As a result, instead of the horribly inefficient copying that it looks like, you end up with a really fast return with no copying at all.


(1) The stack is actually only local to expressionParser so I KNOW I can remove the new keyword

correct

(2) Do I HAVE To create a pointer to the queue in this case. If I need to create the pointer, then I should call a delete output in my query function to properly get rid of the queue?

correct. Here, "creating pointer" doesn't mean again using new and copy all contents. Just assign a pointer to the already created queue<string>. Simply delete output; when you are done with it in your query().

(3) vector being returned by query. Should that be left as I have it or should it be a pointer and what's the difference between them?

I would suggest either you pass the vector<Line*> by non-const reference to your query() and use it or return a vector<Line*>* from your function. Because difference is, if the vector is huge and return by value then it might copy all its content (considering the worst case without optimization)

(4) Once I consume that vector (display the information in it) I need it to be removed from memory, however, the pointers contained in the vector are still good and the items being pointed to by them should not be deleted. What's the best approach in this case?

Pass the vector<Line*> into the function by reference. And retain it in scope until you need it. Once you are done with pointer members 'Line*inside it, justdelete` them

(5) What happens to the contents of containers when you call delete on the container? If the container held pointers?

Nothing happens to the pointer members of the container when it's deleted. You have to explicitly call delete on every member. By the way in your case, you can't delete your vector<Line*> because it's an object not a pointer & when you delete output;, you don't have to worry about contents of queue<> because they are not pointers.

(6) Those pointers are deleted won't that affect other areas of my program?

Deleting a valid pointer (i.e. allocated on heap) doesn't affect your program in any way.


You got a logic problem here - a std::vector (or any container for that matter) of pointers will, at destruction, only kill those pointers, not what they point to. So the following is perfectly fine:

#include <vector>
#include <iostream>

std::vector<int*> GetIntPtrVec(){
  std::vector<int*> ret(10); // prepare for 10 pointer;
  for(int i=0; i<10; ++i){
    ret[i] = new int(i);
  }
}

int main(){
  std::vector<int*> myvec = GetIntPtrVec(); // copies the return into myvec
//                  ^^^^^^^^^^^^^^^^^^^^^^

  for(int i=0; i<myvec.size(); ++i){
    // display and delete again
    std::cout << myvec[i] << "\n";
    delete myvec[i];
  }

  std::cin.get();
}

When returning a vector from a function, it gets copied into the receiving variable (marked by the ^ above).
Then for the pointer-to-vector approach, of course you need to delete that pointer again sometime - when you can be sure that nobody uses it anymore. But that same problem also applies for the first approach - when are you gonna delete the pointers inside the returned vector? You have to make sure that there are no dangling pointers, e.g. pointer, which point to your now deleted memory.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜