-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added copy constructor, move constructor and copy assignment operator. #826
Conversation
Hi there, Thanks for the contribution. Could you please add some tests for these? Take a look at some tests I recently wrote in a different branch: https://github.com/rcurtin/mlpack/blob/bindings/src/mlpack/tests/lsh_test.cpp#L840 Once there are working tests I'll give it a full review. |
…perator and move assignment operator.
…perator and move assignment operator.
@rcurtin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding the tests. There are still a couple issues to take care of before we can merge this, though.
|
||
//Copying the children | ||
left = obj.left; | ||
right = obj.right; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't you simplify this to left = new DTree(*obj.left);
and the same for the right?
BOOST_REQUIRE(tree2.Left()==leftChild); | ||
BOOST_REQUIRE(tree2.Right()==rightChild); | ||
BOOST_REQUIRE(tree3.Left()==leftChild); | ||
BOOST_REQUIRE(tree3.Right()==rightChild); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point leftChild
and rightChild
should be invalid pointers; I bet valgrind will call this an invalid memory access and in some cases this test will fail.
|
||
//Checking children were safely moved | ||
BOOST_REQUIRE(tree2.Left()==leftChild); | ||
BOOST_REQUIRE(tree2.Right()==rightChild); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue with leftChild
and rightChild
here.
template <typename MatType, typename TagType> | ||
DTree<MatType, TagType>& DTree<MatType, TagType>::operator=(const DTree<MatType, TagType>& obj) | ||
{ | ||
if (this != &obj){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think these checks are necessary. If that is true, the user is doing something pretty stupid and we shouldn't need to handle it.
…d move assignment operator.
…d move assignment operator.
@rcurtin |
alphaUpper(obj.alphaUpper), | ||
left(obj.left), | ||
right(obj.right) | ||
{ /* Nothing to do. */ } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't actually copy the left and right child nodes, but your copy operator does. Can you fix that please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rcurtin The child node (data) needs to be copied or the pointer to the child node?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the copy constructor, so you need to copy the data. Take a look at the copy constructor test you wrote---if you just copy the pointer value, then when you delete the tree you copied from, suddenly the left and right pointers point to invalid memory. I bet that if you run valgrind to check for memory leaks on that test, like
$ bin/mlpack_test -t DETTest/CopyConstructorAndOperatorTest
then valgrind will throw invalid memory access errors everywhere, even if the test itself does not fail.
obj.bucketTag = -1; | ||
obj.alphaUpper = 0.0; | ||
obj.left = NULL; | ||
obj.right = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to do this as an initialization list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually had seen those links before creating the PR and had used initialization list in copy constructor. I saw them again now. What confused me was that at line 249 you commented that one can use initialization list for the same . But there we are trying to set the default values of the field of object passed (obj - the one on RHS ) and not of the object created.
What I understood from the link was that initialization list can be used to initialize the fields of the object that is being created (the one for which the constructor has been called - on LHS).
I think I haven't still understood what exactly was said here. I have tried to fix other bugs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah---you can use an initialization list in this method to set the values of the object being constructed. You are right that you can't do that with the RHS, but that isn't what I meant. Sorry for the confusion.
template <typename MatType, typename TagType> | ||
DTree<MatType, TagType>& DTree<MatType, TagType>::operator=(DTree<MatType, TagType>&& obj) | ||
{ | ||
if (this != &obj){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the test to see if this is the same as the object being copied is unnecessary.
ratio = std::move(obj.ratio); | ||
logVolume = std::move(obj.logVolume); | ||
bucketTag = std::move(obj.bucketTag); | ||
alphaUpper = std::move(obj.alphaUpper); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't bother with std::move()
for primitive types like int
, double
, etc., since move will degrade to copies for primitive types. As a result, using move()
there can actually make the code more confusing, because we can't assume that the rvalues will be set to default values or something. (That is my current understanding of the spec.)
@rcurtin |
if(obj.right != NULL) | ||
right = new DTree(*obj.right); | ||
else | ||
right = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can write these in the initialization list cleverly: left((obj.left == NULL) ? NULL : new DTree(*obj.left))
, for instance.
if(obj.right != NULL) | ||
right = new DTree(*obj.right); | ||
else | ||
right = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens when left
and right
are not NULL
when this method is called?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think they are supposed to be freed.
|
||
//Moving children | ||
left = std::move(obj.left); | ||
right = std::move(obj.right); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you're just moving pointers here, you don't actually need to use std::move()
, you can just do left = obj.left
and right = obj.right
.
The AppVeyor failure is a compilation error; if you dig through the log and search for "error " you can see what the issue is:
So although gcc lets you get away with accessing a private member of |
@rcurtin I tried to fix the issues. (I should have gone through AppVeyor log more carefully.) |
Thanks for taking care of all the issues. Did you have anything else to modify or should I merge this? |
I can't think of anything else to modify. If there are no issues left, may be this should be merged. :) |
Ok, merged in. I made some style fixes in 547e2cb. Thanks for your hard work on this PR. Would you like to add your name and email to the list of contributors in |
@rcurtin I would like to add my name and email to the list, would I need a different PR for that ? |
Yeah---this one is already merged so you'll need to open another one for that. |
Okay . |
I tried to add copy constructor, move constructor and copy assignment operator to DTree.
Function declarations are present in dtree.hpp and function definitions are added in dtree_impl.hpp
Token number #817 .
Waiting for review and suggestions to fix the issue.