Introduction
In C++, object instantiation is a fundamental concept that allows us to create instances of classes. However, developers often encounter errors related to incorrect syntax or misunderstanding of how the compiler interprets code. A common error message encountered is: "request for member ‘…’ in ‘…’ which is of non-class type". This tutorial will explore this issue and provide solutions with clear explanations and examples.
Understanding Object Instantiation
Basic Class Definition
Consider a simple class definition:
class Foo {
public:
Foo() {}
Foo(int a) {}
void bar() {}
};
In this class, there are two constructors: one default constructor Foo()
and another parameterized constructor Foo(int a)
.
Instantiating Objects
To create an object of the class Foo
, you can use either constructor:
-
Using the Parameterized Constructor
Foo foo1(1);
-
Using the Default Constructor
Here’s where confusion often arises:
Foo foo2();
Common Error with Member Access
When using Foo foo2();
, the compiler interprets this as a function declaration rather than an object instantiation. This is because of C++’s syntax, which allows for both declarations and definitions without parentheses if no arguments are involved. As a result, trying to access a member function (e.g., foo2.bar();
) leads to the error:
request for member 'bar' in 'foo2', which is of non-class type 'Foo ()()'
Correct Object Instantiation
To correctly instantiate an object using the default constructor without causing ambiguity, remove the parentheses:
Foo foo2;
This tells the compiler explicitly that you are declaring a variable foo2
of type Foo
, not declaring a function.
Example: Correct Usage
Here’s how to properly create and use objects of class Foo
:
#include <iostream>
class Foo {
public:
Foo() { std::cout << "Default Constructor\n"; }
Foo(int a) { std::cout << "Parameterized Constructor with value: " << a << "\n"; }
void bar() { std::cout << "bar() called\n"; }
};
int main() {
// Using parameterized constructor
Foo foo1(1);
foo1.bar();
// Correct usage of default constructor
Foo foo2;
foo2.bar();
return 0;
}
In this example, both foo1
and foo2
are correctly instantiated. The output will demonstrate that both constructors and the member function bar()
are invoked as expected.
Additional Considerations
Pointers to Objects
When dealing with pointers to objects, ensure correct dereferencing when accessing members:
MyClass* myPointerToClass = new MyClass();
myPointerToClass->aMethodOfThatClass(); // Correct syntax using arrow operator
Attempting myPointerToClass.aMethodOfThatClass()
would be incorrect because it treats the pointer as an object.
Iterators and Class Members
When working with iterators or pointers, ensure proper dereferencing:
if ((*class_iter)->num == *int_iter) {
// Correct usage for accessing class member through iterator
}
This ensures that you are calling a method on the pointed-to object rather than treating it as a function.
Conclusion
Understanding C++ syntax nuances is crucial to avoid common pitfalls like misinterpreting variable declarations as functions. By removing unnecessary parentheses when instantiating objects with default constructors, developers can prevent such errors and ensure their code behaves as intended. This tutorial aims to clarify these concepts and help you write more robust C++ programs.