Reflection on C++ Objects and Object Pointers Initialisation
The Initial Code
I have a Student class with Student.h and Student.cpp. I worked with them in order to do the exercise
- Define a class
Studentthat contains member variablesnameandscore - And define a member function
printInfoto print the student’s information. - In the
mainfunction, dynamically create aStudentobject and call theprintInfofunction through a pointer. - Hint: Use
newanddelete.
My main.cpp
1 |
|
My Student.h
1 |
|
I bumped into compilation error like this
1 | ObjectPointerExercise % g++ Student.cpp Car.cpp main.cpp -o myProgram |
At first I thought it was due to compiler standards variation. But I found even in C++11 (or later), my code:
1 | Student* student_ptr = new Student{"Peter", 150}; |
Had issues because:
- The constructor Student(
std::string,int) expects astd::string, but “Peter” is aconst char*. - Brace initialization (
{}) is stricter than parentheses (()) about implicit conversions. Theconst char*tostd::stringconversion, while valid, may be rejected by{}unless explicitly allowed (e.g., by constructing std::string(“Peter”)). - Pre-C++11, this wouldn’t work at all because
{}couldn’t call any constructor—Student isn’t an aggregate, and no rule allowed{}to invokeStudent(std::string, int).
If we were using a pre-C++11 compiler (unlikely, but possible if no -std=c++11 flag was set), Student{"Peter", 150} would fail outright because brace initialization for non-aggregates wasn’t defined.
So even with my extra flag -std=c++11, there still issues in my code:
- Using temporary to initialise an object pointer
- Still have incompatibility with constructor parameter and arugments (
const char*andstd::string)
It reflects my ignorance of following knowledge
- The brace initialiser (list initialiser, unified initialiser) compatibility in Early C++
- The actual concept of string literal
- Reference could work with temporary, but pointer has limitation on it
All Traps about String Literal "Peter"
String Literal Type in C++
- In C++, a string literal like “Peter” is an array of constant characters with type const char[N], where N is the length of the string plus one (for the null terminator
\0). - For
"Peter"- It’s an array of 6 characters: {‘P’, ‘e’, ‘t’, ‘e’, ‘r’, ‘\0’}.
- Its type is const char[6].
- The const qualifier ensures the string literal cannot be modified (e.g., attempting
*"Peter" = 'X'is undefined behavior).
- In C++, a string literal like “Peter” is an array of constant characters with type const char[N], where N is the length of the string plus one (for the null terminator
Array-to-Pointer Decay
- In most contexts, an array type like const char[6] decays to a pointer to its first element, i.e.,
const char*. - When I pass
"Peter"as an argument (e.g., inStudent{"Peter", 150};), C++ treats it as aconst char*pointing to the memory location of ‘P’. - This decay happens automatically in function calls, assignments, and other expressions, unless the array is used in a context that preserves its array nature (e.g.,
sizeofor binding to a reference to an array).
- In most contexts, an array type like const char[6] decays to a pointer to its first element, i.e.,
Why Not
std::string?A string literal (
"Peter") is not astd::string.std::stringis a C++ standard library class that manages a dynamically allocated character buffer, with methods for string manipulation.To create a
std::stringfrom aconst char*, C++ requires a constructor call of string class:1
std::string s = "Peter"; // Implicit conversion via std::string constructor
The
std::stringclass has a constructor that accepts aconst char*std::string::string(const char* s);However, this conversion doesn’t happen automatically when the compiler evaluates “Peter” in isolation. The compiler sees
"Peter"as aconst char*(after array decay) and only converts it tostd::stringif we explicitly call string constructor to convert it to string.
My Specific Case
Insight: String literals in C++ have type
const char[N](e.g.,const char[6]for “Peter”), which decays toconst char*in most contexts. TheStudentconstructor expected astd::string, requiring an implicit conversion. Brace initialization is stricter than parentheses (()) about such conversions, often rejecting them to prevent unintended type coercions.Test 1 : I tried parentheses initialization:
1
Student* student_ptr = new Student("Peter", 150);
This compiled, suggesting that
{}was indeed the issue. Parentheses allowed theconst char*tostd::stringconversion (implicit conversion), but{}didn’t.Test 2: I tried explicitly call string constructor to make sure I pass a string
1
Student* student_ptr = new Student{std::string{"Peter"},150};
This compiled, suggesting that
{}was still indeed the issue. With those approach to keep it, I have to explictly do the conversion to avoid ambiguity.Converting
const char*tostd::stringis a user-defined conversion (via thestd::stringconstructor), and{}may reject a string literal who decayed to constant chararcter pointer to avoid ambiguity or unintended conversions.This is why the compiler reported:error: no matching constructor for initialization of 'Student'. I noted this and moved to the next error.
Why Brace Initialisation Fails in Earlier C++
Brace initialization ({}), introduced in C++11, follows these rules:
- Check for a constructor taking
std::initializer_list. - Look for a constructor whose parameters match the types in {}.
- If the class is an aggregate, perform aggregate initialization.
For Student{"Peter", 150}; in C++98/C++03 (pre-C++11 standards, my default compiler without extra flagstd=c++11) :
Uniform initialization is a C++11 feature. Pre-C++11,
{}was for aggregates only, explaining why older code avoids it for constructors. A.K.A. Brace initialization was only for aggregates (classes without user-defined constructors) or arrays.Click here to check earlier Debugging Note&Reflection about Aggregate Type
Student{"Peter", 150}wouldn’t work at all becauseStudenthas a constructor, making it non-aggregate, and {} couldn’t call constructors. Only()can call constructor in thiis older standards.I can fix it with explicitly define another constructor accepting
cosnt char*type as parameter, if I have to stick to specific old compiler, and I can use()to substitute.
Temporary Objects Are Dangerous
Compiler warned me that I could use that temporary.
1 | Student* student_mry = &(Student{"Mary", 120}); |
Analysis: In C++, Student{"Mary", 120} creates a temporary object (a prvalue), which is destroyed at the end of the full expression. I recalled that I learned that constant reference could work with temporary while non-const reference couldn’t be bind to a fleeting,unamed object which also known as temporary object. Click here to check rules of temporary in C++. Taking temporary’s address with address-of operator & is illegal because the object no longer exists after the statement, leaving student_mry as a dangling pointer. I also stuck in plight with dangling pointer Click here to review the dangling pointer Note&Reflection
Fix: I realised I should dynamically allocate student_mry like student_ptr, using new:
1 | Student* student_mry = new Student("Mary", 120); |
Or initialise an Student objct with variable name, taking its address to make my object pointer
1 | Student mary = Student{string{"Mary"},120}; |
Conclusion
- Always pair
newwithdelete, and don’t forget to assignnull_ptrto the pointer for avoiding leaks - Don’t forget add
std=c++11flag, if I used lots of modern C++ features - Never be ignorant of how the OG C++ works
- The string literal concept and how it works under the hood
- The decay mechinic of char arrays
- How to call constructor of string to initialise a string type variable by passing string literal
- If I have to stick on older compiler, remember use
()as precaution
- Dangling Pointer Issue
- Work with temporarys carefully
- Title: Reflection on C++ Objects and Object Pointers Initialisation
- Author: Ricardo Pu
- Created at : 2025-04-12 18:19:35
- Updated at : 2025-04-12 19:59:48
- Link: https://ricardopotter.github.io/RicardoBlog/2025/04/12/Reflection-on-CPP-Objects-and-Object-Pointers-Initialisation/
- License: This work is licensed under CC BY-NC-SA 4.0.