but want to learn c++
So you while you already have an appropriate answer to the problem itself, I might give you some additional hints...
At first, well, you discovered already, you can pass the argument to the pointer's constructor itself:
this->Age = new int(Age);
this->Age = new int{*a.Age};
Solely: there are two ways of calling a constructor, the classic one with parentheses and the new, uniform initialisation via braces. While the latter showed some good intent, I personally consider it broken and don't use it in my own code (favourite example: std::vector<int>{1, 2, 3}
calls the std::initializer_list
constructor, std::vector<int>{7}
calls classic constructor creating a vector with seven elements; compare to: std::vector<int>(7)
and std::vector<int>({7})
, the latter explicitly calling the std::initializer_list
constructor). Well, you might want to dig a bit further into the topic and decide yourself if you want to follow me or the ones in favour of UI, but whatever you opt for, you should use it consistently and not switch in between the two...
Then get used to use the constructors initialiser list (not to be confused with std::initializer_list
!):
dyn::dyn(std::string Name, int Age)
: Name(Name)
// ^ function argument
// ^ member
{
// ...
}
Well, it even is possible to use the same identifiers (position makes clear which one is which), still you might prefer to use differing ones. There isn't much difference for the native types (int, double, pointers, ...), for complex types, though, the matter changes:
dyn::dyn(std::string Name, int Age)
// default constructor for Name is s t i l l called!
{
this->Name = Name; // and now you do the assignment!
}
Both of default constructing and assignment might cost more or less, in any case, direct initialisation, as occurs by using the initialiser list as shown before, is more efficient. Additionally, references and non-default-constructible types only can be initialised this way.
Then argument type: C# decides by type if an argument is passed by value or by reference; in C++, you can pass any type by reference or by value (or, third option, via pointer), but you need to be explicit about!
dyn::dyn(std::string Name, int Age) // by value (i. e. you make a c o p y of!)
dyn::dyn(std::string& Name, int Age) // by reference
dyn::dyn(std::string* Name, int Age) // by pointer
dyn::dyn(std::string&& Name, int Age) // for completeness: r-value reference
Last one is a new concept allowing move semantics (i. e. moving the contents from one object into another one). There's a good tutorial already, so I won't address further.
However, by accepting a reference, you avoid one unnecessary copy (and as you do not intend to change the argument, it should be const
):
dyn::dyn(std::string const& Name, int Age);
Then have a look at the rule of three. It is true that you get a default assignment operator defined by C++, but it will simply do the following:
dyn::operator=(dyn const& other)
{
Name = other.Name;
Age = other.Age; // now both objects will point to the same address
// and you'll get a double deletion error!
}
OK, there's move-assignment defined, too, but that won't change anything for pointers. With move semantics, the Ro3 got extended to the rule of five; while the former is (still) mandatory, the latter isn't, but you are missing a great optimisation possibility if you don't follow it...
Modern C++ way dealing with dynamically allocated memory is by use of smart pointers, there are std::unique_ptr
,std::shared_ptr
and std::weak_ptr
(the latter one far less commonly used).
class dyn
{
private:
std::unique_ptr<int> Age;
public:
dyn(int Age) : Age(std::make_unique<int>(Age)) { }
};
And guess what: now default generated destructor, move and copy constructors and assignment all are already fine, you don't need to implement any of these explicitly...
return
is not a function, so you shouldn't use parentheses around the returned value. In C++, this even can have concrete effects:
- It prevents copy elision on return values.
- In a rather specific scenario you even can create a dangling reference:
decltype(auto) f() { int n; return (n); } // returns a reference to n!
Finally, one last note about naming conventions. Usually, class identifiers have a leading capitlal letter, where variables (members, globals and local ones) start with a small letter. Function identifiers are ambigous, there are some favouring capital letters (a convention mainly coming from Microsoft), others prefer the initial minuscules still today (which is the far older convention and still the more commonly followed one). For functions, you should opt for one or the other, but in any case, follow it consistently.