Skip to content

My solutions for the exercises#8

Open
KITbutler wants to merge 12 commits intohegner:masterfrom
KITbutler:master
Open

My solutions for the exercises#8
KITbutler wants to merge 12 commits intohegner:masterfrom
KITbutler:master

Conversation

@KITbutler
Copy link
Collaborator

Modified source code for the "modern C++" exercises (except smart_pointers) and the "debugging" exercise. The rangeLoop function added in classical_looping.cpp contains a "Python range"-style start and stop input.

@@ -21,14 +21,13 @@ class Student {

Student* getStudent(){
Student* myStudent = new Student(10801983,"Mary Wright");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given this new statement and seeing no delete anywhere this code has a memory leak.


void iteratorLoop(std::vector<Tool>& tools){
for(std::vector<Tool>::const_iterator tool = tools.begin(), end = tools.end(); tool != end; ++tool){
for(auto tool = tools.begin(), end = tools.end(); tool != end; ++tool){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto tool creates an unecessary copy of each object. Did you consider references?
In addition the const_iterator is const, while auto alone does not preserve constness.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly does auto tool copy? The input vector itself is already passed by reference.

How can tool be const when it is incremented? Supposedly const_iterator implies the target to be const, which is not consistent with the declaration of the function input.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What you get in hand is the object, not an iterator to it. auto tool copies every single tool you are looping over. auto& tool would prevent that.
And as you are dealing with the object and not an iterator, it can as well be const.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The iterator increment ++tool does not work with const, auto seems to favour the normal iterator structure over const_iterator.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah. sorry. you are right. I was having a different example in my head :-)

void rangeLoop(std::vector<Tool>& tools, const int beginning, const int ending){
auto tool = tools.begin();
std::advance(tool,beginning);
for(auto end = tools.end(); std::distance(tool, end) > std::distance(tools.begin(), end) - ending; ++tool){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here std::distance is being calculated during every loop step. Is there a more performant way?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second distance could definitely be stored. The first one might behave differently if the loop content alters the size of tools or shifts tool. For our simple print this is of course no concern so a full pre-calculation would be more efficient.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yap. That is completely correct.

// launch a thread to increment the counter
std::thread increment(incrementCounter,&counter,100000);

std::thread increment([](int* counter, const unsigned int times){for (unsigned int i=0; i<times;++i){++(*counter);}},&counter,100000);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The number of iterations one could as well try to catch by value in the [] part of the lambda.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can that syntax catch an argument passed by std::thread or would this require a pre-initialisation of a variable to catch? At the very least a variable initialisation adds another line of code where, to my understanding, the current implementation does not sacrifice performance.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To put your point to the extreme - if you specify that number in the same line as the lambda, you don't even need it as extra function parameter of the lambda ;-) Thus having an extra variable seems to better match the lambda definition. Nevertheless the current code is correct.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which means the "better" solution depends on the intended purpose of the count. An external variable is "future-proof" for end-user input while a deliberately hardcoded value is easier to track within the function definition. Passing a fixed value as an argument as in the current version would only be desirable if it is reused multiple times within the function, correct? (then again such a function probably should not be an inline lambda to begin with)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing to add to that analysis :-)


std::thread increment([](int* counter, const unsigned int times){for (unsigned int i=0; i<times;++i){++(*counter);}},&counter,100000);
const unsigned int times = 100000;
std::thread increment([&times](int* counter){for (unsigned int i=0; i<times;++i){++(*counter);}},&counter);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would have taken it by value, but OK

int main(){
Student* myStudent = getStudent();
printStudent(myStudent);
delete myStudent;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could have used a smart pointer here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to do this without overwriting all standard pointer usage in the code? Initialising as a smart pointer type breaks printStudent as it expects a raw pointer as input.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants