Question

C++ new Expression: Difference Between `new Test` and `new Test()`

cppconstructorinitializationnew-operatorc++-faq

Question

In C++, if Test is a normal class, is there any difference between these two expressions?

Test* test = new Test;

and:

Test* test = new Test();

I want to understand whether the parentheses after the type name change how the object is created or initialized.

Short Answer

By the end of this page, you will understand how new Type and new Type() behave in C++, especially for classes. You will learn the difference between default initialization and value initialization, why the distinction matters more for built-in types than for ordinary classes, and how this affects real code.

Concept

In C++, new does two things:

  • allocates memory
  • initializes an object in that memory

The important part here is how the object is initialized.

For class types

If Test is a regular class type, these are often equivalent:

Test* a = new Test;
Test* b = new Test();

In many ordinary cases, both expressions call the class's default constructor.

That is why for a typical class, you usually do not see a practical difference.

Why people still care about the difference

The difference becomes important because C++ has several initialization rules:

  • default initialization: new Test
  • value initialization: new Test()

For class types, value initialization usually still ends up using the default constructor, so the result may look the same.

For non-class types such as int, the behavior is very different:

int* a = new int;   // uninitialized
int* b = new int(); // initialized to 0

So the parentheses are not just decoration. They choose a different initialization form.

Why this matters

Initialization bugs are a common source of errors in C++. Understanding the difference helps you:

  • avoid uninitialized values
  • write clearer code
  • understand constructor behavior
  • read older and newer C++ code correctly

In short: for many ordinary classes, there is no visible difference, but the parentheses do represent a different initialization rule.

Mental Model

Think of new as renting a storage unit and then deciding what to place inside it.

  • new Test means: rent the space and initialize it normally for that type
  • new Test() means: rent the space and explicitly ask for value initialization

For a class, both often result in calling the same default constructor, so the storage unit ends up containing the same kind of object.

For a primitive type like int, the difference is bigger:

  • new int = you get a box with whatever was already there
  • new int() = you get a box reset to zero

So the parentheses are like saying, "Please initialize this in the safer, explicit way."

Take Quiz

Syntax and Examples

Core syntax

Type* p1 = new Type;
Type* p2 = new Type();

Example with a class

#include <iostream>
using namespace std;

class Test {
public:
    Test() {
        cout << "Test constructor called\n";
    }
};

int main() {
    Test* a = new Test;
    Test* b = new Test();

    delete a;
    delete b;
}

What happens?

Both lines create a Test object dynamically and both call the default constructor.

Example with a built-in type

#include <iostream>
using namespace std;

int main() {
    int* a = new int;
    int* b = new int();

    cout << *b << "\n"; // guaranteed to be 0

    delete a;
    delete b;
}

Important note

  • new int gives an uninitialized int
  • new int() gives a zero-initialized int

Reading *a before assigning to it would be undefined behavior.

Example with member data

#include <iostream>
using namespace std;

class Data {
public:
    int x;
};

int main() {
    Data* a = new Data;
    Data* b = new Data();

    // a->x is indeterminate
    // b->x is zero-initialized in this case

    delete a;
    delete b;
}

This example shows why initialization rules matter, even for class-like types.

Step by Step Execution

Consider this code:

#include <iostream>
using namespace std;

class Test {
public:
    Test() {
        cout << "constructed\n";
    }
};

int main() {
    Test* p1 = new Test;
    Test* p2 = new Test();

    delete p1;
    delete p2;
}

Step by step

Test* p1 = new Test;

  1. Memory is allocated for one Test object.
  2. Test is default-initialized.
  3. Because Test is a class with a default constructor, that constructor runs.
  4. p1 stores the address of the new object.

Test* p2 = new Test();

  1. Memory is allocated for one Test object.
  2. Test is value-initialized.
  3. For this class, that still results in the default constructor running.
  4. p2 stores the address of the new object.

Visible result

Both lines print:

constructed
constructed

So in this common class example, the runtime effect appears the same.

Real World Use Cases

1. Creating objects dynamically

When you need an object to outlive the current scope, you may allocate it dynamically:

Test* service = new Test();

This was more common in older C++ code and still appears in legacy systems.

2. Initializing primitive data safely

If you dynamically allocate numbers, counters, or flags, initialization matters:

int* counter = new int();

This ensures the counter starts at 0.

3. Working with simple structs or data containers

struct Config {
    int port;
    bool debug;
};

Config* cfg = new Config();

This can zero-initialize members in useful cases, preventing garbage values.

4. Reading and maintaining older C++ code

You will often see both forms in real codebases. Knowing the difference helps you understand whether the author intended explicit initialization or not.

5. Resource-owning objects

Even though modern C++ prefers smart pointers, you may still see:

auto ptr = new Test();

Understanding initialization is still necessary when reviewing or modernizing such code.

Take Quiz

Real Codebase Usage

In real C++ projects, developers usually care less about the visual difference between new T and new T() for classes, and more about safe initialization and ownership.

Common patterns

Prefer automatic storage when possible

Test test;

This avoids manual memory management.

Prefer smart pointers over raw new

#include <memory>

auto test = std::make_unique<Test>();

This is safer because ownership is clear and memory is released automatically.

Use explicit initialization for simple data

auto count = std::make_unique<int>(0);

In modern code, developers prefer being explicit rather than relying on subtle initialization rules.

Validation and setup in constructors

For classes, the constructor often performs setup, validation, or resource acquisition. In those cases, both new Test and new Test() may behave the same because the constructor defines the object's initial state.

Guarding against uninitialized members

Developers often initialize members directly inside the class:

class Config {
public:
    int port = 0;
    bool debug = false;
};

This reduces confusion about whether new Config or new Config() was used.

Common Mistakes

Mistake 1: Assuming the parentheses never matter

They often do not matter for ordinary classes, but they do matter for built-in types.

int* a = new int;   // uninitialized
int* b = new int(); // zero-initialized

Avoid it

Use explicit initialization when you need a known starting value.

Mistake 2: Reading uninitialized memory

Broken code:

int* x = new int;
std::cout << *x << "\n"; // undefined behavior

Avoid it

Initialize the value:

int* x = new int();

or

int* x = new int(42);

Mistake 3: Forgetting that class members may still be uninitialized

Broken code:

class Data {
public:
    int value;
};

Data* d = new Data;

Here, value may be uninitialized.

Avoid it

Initialize members directly in the class or constructor:

class Data {
public:
    int value = 0;
};

Mistake 4: Forgetting to delete

Broken code:

Test* t = new Test();
// forgot delete

Avoid it

Prefer smart pointers:

auto t = std::make_unique<Test>();

Mistake 5: Thinking new Type() always means "call constructor with no arguments"

It means value initialization, not just "empty parentheses." For classes, that may call the default constructor, but the underlying rule is broader than that.

Comparisons

ExpressionInitialization kindClass type behaviorBuilt-in type behavior
new TDefault initializationUsually calls default constructorUninitialized
new T()Value initializationUsually calls default constructorZero-initialized
new T(args)Direct initializationCalls matching constructorInitializes with given value

new Test vs new Test()

For an ordinary class with a default constructor:

Test* a = new Test;
Test* b = new Test();

Usually no practical difference is visible.

new int vs new int()

int* a = new int;
int* b = new int();

This difference is important:

  • a points to an uninitialized int
  • b points to 0

Raw new vs smart pointer creation

ApproachMemory managementRecommended in modern C++
new Test()Manual delete requiredUsually no
std::make_unique<Test>()Automatic cleanupYes
std::make_shared<Test>()Shared ownershipSometimes
Take Quiz

Cheat Sheet

Quick reference

T* p = new T;     // default initialization
T* p = new T();   // value initialization
T* p = new T(42); // direct initialization

Main rule

  • For many ordinary classes, new T and new T() behave the same.
  • For built-in types, they can behave very differently.

Examples

int* a = new int;   // uninitialized
int* b = new int(); // 0
class Test {
public:
    Test() {}
};

Test* a = new Test;   // calls default constructor
Test* b = new Test(); // also calls default constructor

Safer modern alternative

auto p = std::make_unique<Test>();

Things to remember

  • new allocates memory and initializes an object.
  • Parentheses change the initialization form.
  • Do not read uninitialized values.
  • Prefer constructors or member initializers for clear object state.
  • Prefer smart pointers over raw new in modern C++.

FAQ

Does new Test() always call the constructor?

For a class type with a default constructor, yes, it usually calls that constructor.

Is new Test the same as new Test() for every type?

No. They are often equivalent for ordinary classes, but not for built-in types like int.

What is the difference between default initialization and value initialization?

Default initialization uses the type's default behavior. Value initialization performs a more explicit initialization and often zero-initializes simple types.

Why does new int() give 0?

Because value initialization of a built-in numeric type zero-initializes it.

Should I use raw new in modern C++?

Usually no. Prefer std::make_unique or std::make_shared unless you have a specific reason.

Can class members still be uninitialized even if I create the object with new?

Yes. If the class does not initialize its members, some members may still have indeterminate values depending on the initialization form and class design.

Which form should I prefer when reading old code?

Understand the intent first. For classes, both may be fine. For primitive or simple data types, () often signals intentional initialization.

Take Quiz

Mini Project

Description

Build a small C++ program that compares dynamic initialization forms for a class and a built-in type. This helps you see when parentheses matter and when they do not.

Goal

Create a program that demonstrates the difference between new T and new T() using both a class and an int.

Requirements

  • Define a simple class with a default constructor that prints a message.
  • Dynamically allocate one object with new Test and another with new Test().
  • Dynamically allocate an int with new int() and print its value.
  • Clean up all dynamically allocated memory with delete.
  • Add comments explaining which initialization form each line uses.
Take Quiz

Keep learning

Related questions

View more