Pointer help

Started by
15 comments, last by Tom Sloper 1 year, 1 month ago

Well I am reading a book on c++. I am doing the exercises in the back of the chapters. I am currently working on learning Pointers. I am trying to iterate through a vector of pointers stored on the heap. I want it to print out the values stored on the heap but it prints out the addresses of the values.

	vector<Person*> person = { new Person(55,"Phil"), new Person(61,"Sylvia"), new Person(65,"Jeff") };
	vector<Car*> car = {};
	for (vector<Person*>::iterator it = person.begin(); it != person.end(); it++)
	{
	cout << *it << endl;
	}
Advertisement

Iterating over a vector manually is done via, well, iterators. An iterator itself is sort-of like a pointer to the value stored in the vector. So, dereferecing the iterator doesn't give you the value of what the pointer points to, but the pointer itself - which, if you print it, prints the address. You'd eigther need to do:

cout << **it << endl;

where you dereference the pointer that you get from the iterator, or you just scrap the 1980s-coding and do it the proper modern and easy way:

for (Person* p : person)
{
	cout << *p << endl;
}

Be adviced that if you have a book on c++ which predates c++11 you are getting into a world of hurt. While (for example)iterators do have a usage outside of for-loops, there is very little merit to learning c++ without all the modern tools, especially those that simply make your life easier without requiring much additional knowledge.

well I tried cout << **it << endl; but it does not work

pbivens67 said:
well I tried cout << **it << endl; but it does not work

Yes that's because the stream out operator has no idea how to print your person class, so it prints whatever is at the memory you are dereferencing. If you give your person class an overloaded stream out operator it will know how to print your person class

Stream out operators look like this:

std::ostream& operator<<(std::ostream& out, const Fraction& f) { return out << f.num() << '/' << f.den(); }

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion

Juliean said:

Be adviced that if you have a book on c++ which predates c++11 you are getting into a world of hurt. While (for example)iterators do have a usage outside of for-loops, there is very little merit to learning c++ without all the modern tools, especially those that simply make your life easier without requiring much additional knowledge.

At the risk of getting into a rathole discussion, I'm going to disagree with that. IMO many times the modern tools are not ideal for every case. As an example, sometimes std::string may not be the best choice. For instance, you don't always want SSO. And in some cases you might want COW strings, or just code a string in place if you know the maximum size. Also, I think more often than not std::shared_ptr downright sucks. Note I didn't say reference counting pointers. Finally, some of the data structures are a black box and again may not be optimal for what you are doing. I think this comes into play once you get out of the learning stage and start trying to produce a fairly large-scale real application. Of course, you can go back and learn the basic stuff, but I find many people don't bother and end up producing some highly suboptimal software and end up digging themselvs into a pit they don't know how to get out of.

Gnollrunner said:
At the risk of getting into a rathole discussion, I'm going to disagree with that. IMO many times the modern tools are not ideal for every case. As an example, sometimes std::string may not be the best choice. For instance, you don't always want SSO. And in some cases you might want COW strings, or just code a string in place if you know the maximum size. Also, I think more often than not std::shared_ptr downright sucks. Note I didn't say reference counting pointers. Finally, some of the data structures are a black box and again may not be optimal for what you are doing. I think this comes into play once you get out of the learning stage and start trying to produce a fairly large-scale real application. Of course, you can go back and learn the basic stuff, but I find many people don't bother and end up producing some highly suboptimal software and end up digging themselvs into a pit they don't know how to get out of.

Oh, I don't disagree with the sentiment. Not every modern tool should be used always, but a lot of them simple pretty much always should. range-based for should always be used unless you cannot (in which case now you probably use ranges). std::unique_ptr should always be used, unless you have a more specific need for manual memory managment (like emplace-newing, ref-counting etc… at least you should never manually new/delete anything!). std::string might indeed not always be ideal, especially due to SSO which I think can often be a pessimissation now with moves - but there is std::string_view which alongside std::span is a great addition that IMHO should always we used when passing non-owning string/array-data.

So at the very least, you should learn those modern tools, and then you can decide whether you need them or not - I hope thats sort of a common ground you can agree on.
In my book though, for an beginner, it would be better if they used std::shared_ptr everywhere VS new/delete like OP has shown, but that might be debatable (and is pretty much just trying to decide on a pick-your poison situation)

Juliean said:


In my book though, for an beginner, it would be better if they used std::shared_ptr everywhere VS new/delete like OP has shown, but that might be debatable (and is pretty much just trying to decide on a pick-your poison situation)

Well …. In general, you can't use reference counting pointers in any data structure with loops. Anything with simple back pointers will fail, and more complex stuff will obviously fail too. Also, I've seen code where people tried to use std::shared_ptr everywhere including in simple function calls and it got pretty nightmarish in places.

Smart pointers mean ownership and using them on a very temporary basis just makes things more confusing and even bug prone. IMO that's something beginners better learn pretty early, because smart pointers aren't as forgiving as a GC. If you come to C++ from C# or Java, you had better realize the difference.

Then we have std::shared_ptr specifically…… First the pointer itself uses twice the memory which is a killer for large data sets. Note that's not required for old school reference counting implementations. Second, unless you know what you are doing, you can end up with separate control blocks. Those control blocks also have a weak counter whether you need it or not and also are thread safe whether you need them to be or not. In general, I find that std::shared_ptr sucks for all but the simplest usages where size and performance don't matter.

I found this to be an interesting video mainly because it has Golang with a GC, beating C++ using std::shared_ptr . I have to believe that the C++ version of this was just poorly written. First it uses std::shared_ptr, and also to really make something like this fast you want to take over the heap, to use slab allocation or something similar, depending on your exact needs.

This kind of gets to my main complaint with Modern C++. While it might make thing ostensibly safer and easier to use for a beginner, it also takes simple concepts and makes them difficult to understand, while simultaneously masking how things actually work. When I learned C++ years ago, nobody considered it a hard language. Now it's one of the major complaints. Note C does not get the same backlash as C++, yet it has most of the supposed downfalls. It's simple enough that most competent programmers can wrap their head around the language without too much trouble. Yes, it requires you to be more careful, as does old C++, but at least you end up understanding what's happening in your code.

I'm willing to bet that if the OP had learned C or even old C++ the way it used to be taught, he never would have had to post this question here to solve a very simple issue. Not that I blame him for that.

Gnollrunner said:
Well …. In general, you can't use reference counting pointers in any data structure with loops. Anything with simple back pointers will fail, and more complex stuff will obviously fail too. Also, I've seen code where people tried to use std::shared_ptr everywhere including in simple function calls and it got pretty nightmarish in places.

Sure. And using raw new/delete is guarnteed to lead to memory leaks, double-deletions, unexpected behaviour etc… So, whats worse? Personally, I'd just do

	vector<std::unique_ptr<Person>> person = { std::make_unqiue<Person>(55,"Phil"), std::make_unqiue<Person>(61,"Sylvia"), std::make_unqiue<Person>(65,"Jeff") };

which is in my book just strictly superior in every way.

Gnollrunner said:
Then we have std::shared_ptr specifically…… First the pointer itself uses twice the memory which is a killer for large data sets. Note that's not required for old school reference counting implementations. Second, unless you know what you are doing, you can end up with separate control blocks. Those control blocks also have a weak counter whether you need it or not and also are thread safe whether you need them to be or not. In general, I find that std::shared_ptr sucks for all but the simplest usages where size and performance don't matter.

Note that before we get too sidetracked - I don't condone nor want to propose using std::shared_ptr exessively in any way. I rarely use it myself, unlike std::unique_ptr.

Gnollrunner said:
I'm willing to bet that if the OP had learned C or even old C++ the way it used to be taught, he never would have had to post this question here to solve a very simple issue. Not that I blame him for that.

See that I don't understand. How can you say that, when OP is obviously learning C++ by a book which was written way before for-each loops and consorts exist? I would argue that he is exaclty learning c++ the way you propose and its exactly the reason why he has to ask this question. Especially early on, certain things like what an iterator does exactly doesn't really matter to the actual understanding of the language. I also btw like about c++ that you are pretty down to the metal and don't have to worry about some runtime doing black-box magic; and extensively can understand lots of wats going on in the code. But there is a time and place for that, and its not when you start learning. Nobody starts learning those manual for-loops incrementing iterators and thinks “a, how nice that I understand whats going on!” - instead its usually “what the fuck was the syntactic crap that I have to copy and paste all the time I want to iterate over a vector?”.

So yeah, I would say that we probably don't disagree on that many thing but more the semantics. Unless you really want to make the point that everything in the newer standards have made c++ more complex or hard to understand. That i vehemently disagree on. If OP had had a book that taught range-based for and unique_ptrs, he wouldn't have had to ask that question (well minus the missing << operator), and probably a lot of questions coming up. If you think otherwise we'll just have to agree to disagree because that is a hill I am surely willing to die on :D And I don't think we should hijack OPs thread too much.

If you are learning the language you should know that you generally want `std::vector<Person>'. Most of the time you want containers of objects, not containers of pointers. Sometimes you may want to have containers of pointers, for instance if you want polymorphic behavior through public inheritance. In that case, you want `std::vector<std::unique_ptr<Person>>'. If you want to learn about raw pointers, I suggest you learn C. In C++ we have much better tools, and it is exceedingly rare that you will want to use raw pointers.

Modern tools in C++ (the new `for' syntax, smart pointers, std::function, lambdas…) do make code much better if used correctly. For example, `std::unique_ptr' allows you to keep track of who owns a pointer, and should be used most of the time when you need a pointer. `std::shared_ptr' should be used when ownership is shared, which should not happen too often. If you use `std::shared_ptr' everywhere so you don't have to think about ownership, you are not using the tools correctly, and you will get in trouble.

I am undecided as to whether you should learn C before C++. I'll argue in favor of the motion first. When I learned C I already knew assembly for a couple of processors, and there is a naive mapping between C code and assembly code. The compiler is free to make many optimizations, but you do have an idea of how things are going to be implemented and what some tradeoffs are (local variables live in registers or on the stack; global variables live in the data segment; parameters are generally passed by pushing them to the stack; dynamic memory allocation requires a non-trivial library call; using the stack is naturally friendly to the cache, while with dynamic memory allocation all bets are off; etc.). Then I learned C++98, where I still had some idea of how most things are implemented (I confess I have never spent the time to learn how exceptions are implemented). Then I half-learned modern C++, and I still know how most things are implemented. But there is so much syntactic sugar that if you were to learn modern higher-level C++ first, you would have a really hard time understanding how things work under the hood and what operations are likely to be fast or slow, so it will be hard to make good choices. The argument against learning C first is that most of the time you do want to be writing modern higher-level C++, so perhaps you should learn that directly.

well thanks for all the commentary, I want to only learn the fundamentals first.

This topic is closed to new replies.

Advertisement