Do I need new, delete if I can use C++11 smart pointer?

Asked 2 years ago, Updated 2 years ago, 123 views

C++11 has weak, shared, unique, but is it meaningful to use instance generation using only new and delete in an environment where these can be used?

In C++11, is the method using only new, delete positioned as a method that should not be used?

Backward compatibility should not be considered

c++ c++11

2022-09-30 18:50

4 Answers

Your answers have been updated to new and unique, so I'll explain what happened before update.

You may take it for granted, but

inti0;

When described as , the C-language compatible and uninitialized variable i0 is provided.This is

inti1=int();
inti2 { };

The default constructor runs and initializes with 0.The same thing exists in new

 int*j0 = new int;
int*j1 = new int();
int*j2 = new int{};

j0 remains uninitialized while j1 and j2 are initialized with 0 by the default constructor after memory is reserved.
And the main topic is

 std::unique_ptr<int>k=std::make_unique<int>();

Therefore, the default constructor will be invoked, which will result in a forced initialization cost.

unique_ptr can also be dynamically release().Therefore, the unique_ptr destructor adds a test to see if the pointer it holds has been released.The execution time difference also includes that the hard-coded delete var; statement omits this test.

In summary, the difference in initialization cost during execution?If you don't care about this difference, you should use unique or shared to prevent forgetting to deleteAlso, if initialization is dominant over runtime, you should review the structure rather than worry about the difference between new and unique.

In addition, if you use make_unique(), you will need to call the constructor from make_unique(), so you must make the constructor public.

class Test {
    Test() = default;
public:
    static auto create() {
        return std::make_unique<Test>();// error
    }
};

Although malloc() only secures memory and does not initialize, calloc() clears zero.


2022-09-30 18:50

I thought that one of the reasons for using new might be speed, so I did a verification.Below is the verification result.

#include<chrono>
# include <cstdlib>
# include <cstring>
# include <functional>
# include <iomanip>
# include <iostream>
# include <memory>

US>structure Test {
    intv;
    Test(intv):v(v){};
    int f(inti)
    {
        v+=i;
        return v;
    }
};

auto benchmark(conststd::function<void(int)>&func,const int count=1)
{
    auto start=std::chrono::high_resolution_clock::now();
    for(inti=0;i<count;++i)func(i);
    auto end=std::chrono::high_resolution_clock::now();
    return std:chrono::duration_cast<std:chrono::nanoseconds>(end-start)
        .count();
}

int main()
{
    const int count = 100000;
    intn;
    // stack
    n = 0;
    auto stack_t=benchmark(
        &n (inti){
            Test t(i);
            n + = t.f(i);
        },
        count);
    std::cout<<"stack:"<<std::setw(12)<<stack_t<"ns,"<n
              <<std::endl;
    // malloc
    n = 0;
    auto malloc_t=benchmark(
        &n (inti){
            auto = static_cast<Test*>(std::malloc(sizeof(Test)));
            t->v=i;
            n + = t - > f(i);
            std::free(t);
        },
        count);
    std::cout<<"malloc:"<<std::setw(12)<<malloc_t<"ns,"<n
              <<std::endl;
    // new
    n = 0;
    auto new_t=benchmark(
        &n (inti){
            auto = new Test(i);
            n + = t - > f(i);
            delete;
        },
        count);
    std::cout<<"new:"<<std::setw(12)<<new_t<"ns,"<n
              <<std::endl;
    // unique
    n = 0;
    auto unique_t=benchmark(
        &n (inti){
            auto = std::make_unique<Test>(i);
            n + = t - > f(i);
        },
        count);
    std::cout<<"unique:"<<std::setw(12)<unique_t<"ns,"<n
              <<std::endl;
    // shared
    n = 0;
    auto shared_t=benchmark(
        &n (inti){
            auto = std::make_shared<Test>(i);
            n + = t - > f(i);
        },
        count);
    std::cout<<"shared:"<<std::setw(12)<<shared_t<"ns,"<n
              <<std::endl;
    return 0;
}

*This code is for C++14

Compiled and executed the above code in macOS Apple LLVM version 8.1.0 (clang-802.0.38) with clang++-O0-std=c++14 (without optimization), the results were as follows:

 stack: 2346911ns, 1409965408
malloc —7772341ns, 1409965408
   new —8951401ns, 1409965408
unique —1086486862ns, 1409965408
shared —22686880ns, 1409965408

Unique_ptr and shared_ptr seem to be slower than new because they are wrapped.Furthermore, the slow shared_ptr may be due to reference counter processing.However, when I compiled it with clang++-O2-std=c++14 (with optimization), the results were as follows:

 stack : 270138ns, 1409965408
malloc —306524ns, 1409965408
   new —263298ns, 1409965408
unique —287971ns, 1409965408
shared —8967664ns, 1409965408

Except for shared_ptr, it is almost the same as stuck in the stack.With optimization, new and unique_ptr seem to be almost as fast as the stack.However, since the compiler and code depend on how much optimization is effective, I don't think it's possible to conclude that optimization can achieve the same speed.If you have evidence that unique_ptr may or may not be optimized at a speed equal to new, please provide us with information.

As for shared_ptr, perhaps because of the reference counter mechanism, it seems that it is being delayed.I didn't know if it could be optimized to the same speed as new.

Compiled with Homebrew GCC 6.3.0_1

#g++-6-O0-std=c++14
 stack —2711000ns, 1409965408
malloc —9153000ns, 1409965408
   new —13656000ns, 1409965408
unique —16941000ns, 1409965408
shared —33897000ns, 1409965408
# When g++-6-O2-std=c++14
 stack —229000ns, 1409965408
malloc —251000ns, 1409965408
   new: 7885000ns, 1409965408
unique —7101000ns, 1409965408
shared —8947000ns, 1409965408

Therefore, new and unique_ptr have almost the same results in optimization.

*I don't have the latest VC++ right now, so I would appreciate it if you could edit it and add it.

The conclusion from the validation is that if not optimized, the unique_ptr and shared_ptr will be slower than new, but the unique_ptr will be almost degrees faster and the shared_ptr may remain slower.However, it is difficult to conclude easily because the effectiveness of optimization depends on the compiler and code content.

The original verification code was a problem and should not be used as a reference.For more information, see Sayuri's answer.


2022-09-30 18:50

C++11 has weak, shared, unique smart pointers, but is there any point in using instance generation using only new, delete in an environment where these can be used?

alpha comments, with std::make_unique<T> added, C++14 and later should "avoid new/delete instance generation/destruction if smart pointers can meet the requirements."

In C++11, is the method using only new, delete positioned as a method that should not be used?

I don't think we should use new/delete unless we use the new operator (placement new, alignment specification).

Please also refer to the English SO Are new and delete still useful in C++14?


2022-09-30 18:50

Basically, it should be unified with smart pointers.
However, make_shared is not easy to use in classes where the constructor is not public.

class Foo{
private:
    Foo();

public:
    static std::shared_ptr<Foo>create(){
       return std::make_shared<Foo>();//compilation error
    }
};

That's why

 std::shared_ptr<Foo>(new Foo();;

I think you will be using new.However, many people dislike this and use make_shared using techniques.


2022-09-30 18:50

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.