Reputation: 21
I am having trouble understanding proper usage of the new
keyword. My question is:
delete
?#include <string>
#include <iostream>
struct myOptions {
int myInt;
std::string myString;
};
myOptions* packageOptions() {
myOptions* options = new myOptions;
options->myInt = 42;
options->myString = "hello world";
return options;
}
int main() {
myOptions* options = packageOptions();
std::cout << options->myString << std::endl;
std::cout << options->myInt << std::endl;
delete myOptions; // this just feels wrong to put here
}
My gut is telling me it's bad because the main function shouldn't have to manage the memory allocated by other functions, as in it's breaking some kind of encapsulation. I thought about doing a class constructor/deconstructor, but that seems to be overkill.
Upvotes: 1
Views: 73
Reputation: 2272
What you have demonstrated is correct as far as managing the memory goes. But stylistically and from a maintenance point of view, it could be much better.
Two ways that are easier to maintain are:
Smart pointers were created to help prevent memory leaks, which can be a long term pain to find in large projects. Look into std::shared_ptr
or see the shared pointers suggested answers.
Passing in the actual structure allows the caller to worry about the memory space used, including allocating and deallocating it. This was the preferred way, before smart pointers came along, and is still the rule of thumb (letting the code that needs the data manage the data object associated with it).
Passing in the actual structure would make the code look something like this:
#include <string>
#include <iostream>
struct myOptions {
int myInt;
std::string myString;
};
void packageOptions( myOptions& theInputOptions) {
theInputOptions.myInt = 42;
theInputOptions.myString = "hello world";
}
int main() {
myOptions options;
packageOptions( options );
std::cout << options.myString << std::endl;
std::cout << options.myInt << std::endl;
}
I find this style far easier to maintain the code. It can be combined with smart pointers where needed.
Upvotes: 0
Reputation: 42
Instead of using structures in this manner, try to use classes and constructors/destructors for the same.
Upvotes: 0
Reputation: 39906
Using shared_ptr
is not the best option in your present case. I give this example to show you it exists, and then you don't have to bother about who/when the object is deleted. The shared_ptr
will call the destructor/delete whenever there are no references on the object left, ie at the end of main
in your example. (also shared_ptr has been introduced in C++11, not available in C++03)
#include <string>
#include <iostream>
#include <memory>
struct myOptions {
int myInt;
std::string myString;
};
using OptionsPtr = std::shared_ptr<myOptions>;
OptionsPtr packageOptions() {
OptionsPtr options = std::make_shared<myOptions>();
options->myInt = 42;
options->myString = "hello world";
return options;
}
int main() {
OptionsPtr options = packageOptions();
std::cout << options->myString << std::endl;
std::cout << options->myInt << std::endl;
}
Anyway, in your case, stack allocation, like in @Cyber answer, is much preferable.
Upvotes: 0
Reputation: 101456
From a strictly technical perspective, what you are doing is fine.
But you asked what the proper use of new
is [in C++] -- and the answer may surprise you.
The most proper way to use new
is not to. The same can be said of delete
. Instead of using new
/delete
, you should be using smart pointers such as std::shared_ptr
along with an accompanying make_shared
.
I'm not saying there are no exceptions, but those exceptions would be unusual and typically the result of a design that could be otherwise modified.
In fact, I would also suggest another question altogether: what is the proper use of dynamic allocation? Again, the answer I would suggest is don't use dynamic allocation. Obviously there are exceptions to this as well, in fact more exceptions than the "don't use new" guideline above -- but as you become more experienced both with the syntax and the semantics of the C++ language, you will find that dynamic allocation is needed in fewer scenarios.
Upvotes: 0
Reputation: 283624
The delete
keyword should only appear inside the implementation of smart pointer classes. You can either return by value as Cyber suggested, or in cases where that isn't ideal (for example, return by value causes slicing of derived types) you can return std::unique_ptr
and store it in a local variable of the same type, and the destructor will automatically clean up the object and its memory when the pointer goes out of scope.
"Doing a class constructor/destructor" for each case would be overkill. Just take advantage of the existing highly reusable smart pointers.
Upvotes: 1
Reputation: 117856
There is no reason to have to chase around memory manually as you are doing. I would just declare your variable on the stack, and return it by value. Then let RAII clean up the memory for you when the variable falls out of scope.
myOptions packageOptions() {
myOptions options;
options.myInt = 42;
options.myString = "hello world";
return options;
}
int main() {
myOptions options = packageOptions();
std::cout << options.myString << std::endl;
std::cout << options.myInt << std::endl;
}
Upvotes: 2