Reputation: 6638
So I want to add a struct from a c header file as a class member to a c++ class.
But I get a compiler error for the cpp
file: bar was not declared inn this scope
.
This is what I have:
// myClass.hpp
#include fileWithStruct.h
class myClass
{
public:
struct foo bar;
};
//myClass.cpp
#include "myClass.hpp"
//Initialize structure in Constrcutor
myClass::myClass( )
{
bar = {1, 0, "someString", 0x4};
}
Upvotes: 47
Views: 129153
Reputation: 32904
#include "fileWithStruct.h"
/* say the contents were
struct foo
{
int foo1;
float foo2;
};
*/
class myClass
{
public:
int val;
foo bar;
// since foo is a POD-struct (a.k.a C struct), no constructor would be present
// however bar() will zero-initialize everything in the struct
myClass() : val(), bar()
{
}
};
The parentheses following bar
matters. Refer value and zero-initialization to understand why this works. It is to be noted that by adding a constructor to myClass
, we've made it a non-POD type. To work around this, one can retain myClass
as an aggregate and write:
class myClass
{
public:
int val;
foo bar;
};
int main()
{
myClass zeroed_obj = { };
myClass inited_obj = { 2, {0, 1.0f} };
myClass partially_inited_obj = { 2 }; // equivalent to {2, {}}; which would zero all of myClass::bar
myClass garbage_obj; // warning: when left uninitialized, every member without a constructor will end up with garbage value
}
class myClass
{
public:
// default member initializations
int val = { }; // zero-initialization
foo bar = { 0, 0.0f }; // aggregate-initializing foo here, just giving { } will zero all of myClass::bar
// should you want to receive an element from the constructor, this can be done too
// aggregate initializing a struct in constructor initialization list is allowed from C++11 onwards
// in C++03, we would've resorted to just setting the member of bar inside the constructor body
myClass(int _foo1) : val{}, bar{_foo1, 0.f}
{
}
// since we've a non-default constructor, we've to re-introduce the default constructor
// if we need the above in-class initialization to work
myClass() = default;
};
Here we use C++11's uniform initialization syntax. However, by doing this myClass
becomes a non-POD type; member initialization is akin to adding a constructor to the class, thereby rendering myClass
a non-trivial but standard-layout class. As per C++11 for a class to be POD it should be both trivial and standard-layout. Instead doing
#include "fileWithStruct.h"
#include <type_traits>
#include <iostream>
class myClass
{
public:
int val;
foo bar;
};
int main()
{
myClass obj { }; // initializes val, bar.foo1 and bar.foo2 to 0
myClass m { 0, {1, 2.0f} }; // initilizes each member separately
std::cout << std::is_pod<myClass>::value << std::endl; // will return 1
}
will retain myClass
as a POD.
Refer to this excellent post to know more about aggregates and PODs.
Upvotes: 42
Reputation: 626
Let us consider an example. Consider a Linked List in which each node is represented by:
struct ListNode {
int val;
ListNode *next;
ListNode(int x){
val = x;
next = NULL;
}
};
To initialize a node with value 5 and next pointer pointing to NULL you can write the code snippet as:
ListNode node = new ListNode();
node.val = 5;
node.next = NULL;
or
ListNode node = new ListNode(5);
Another fancy declaration can be made as
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
Here ListNode(int x): val(x), next(NULL)
is a constructor which initializes the value of the struct ListNode.
Hope this make things more clear and easy. :)
Upvotes: 1
Reputation: 104
You need to specify that the foo struct should have "C-linkage". The following is a complete example.
// fileWithStruct.h
#ifdef __cplusplus
extern "C" { // Declare as extern "C" if used from C++
#endif
typedef struct _foo
{
int a;
int b;
const char* c;
int d;
} foo;
#ifdef __cplusplus
}
#endif
The myClass header file:
// myClass.hpp
#include "fileWithStruct.h"
class myClass
{
public:
myClass();
foo bar;
};
The C+11 implementation of myClass which uses extended initializer lists:
// myClass.cpp
#include "myClass.hpp"
myClass::myClass( )
: bar({1, 0, "someString", 0x4})
{
}
... and the C++03 version if you haven't moved to C++11 yet:
#include "myClass.hpp"
myClass::myClass( )
: bar()
{
bar.a = 1;
bar.b = 0;
bar.c = "someString";
bar.d = 0x4;
}
Upvotes: 1
Reputation: 24596
What you are doing there is assignment, not initialization. Initialization happens in the initialization list of a constructor, before the constructor body, or in C++11 in an initializer right after the member variable declaration:
myClass.hpp, general case:
/** you might want to do this if you are linking
* against the C lib or object file of that header:
*/
extern "C" {
#include fileWithStruct.h
}
class myClass
{
public:
foo bar; //no need for "struct" in C++ here
};
myClass.cpp
#include "myClass.hpp"
//Initialize structure in Constrcutor
myClass::myClass( )
: bar{1, 0, "someString", 0x4}
{}
Antoher option is to provide the initial value of foo with an brace-or-equal-initializer at the member variable declaration:
myClass.hpp
extern "C" {
#include fileWithStruct.h
}
class myClass
{
public:
foo bar{1, 0, "someString", 0x4};
};
In this case, you need not define a constructor, since it's generated implicitly by the compiler (if needed), correctly initializing bar
.
Here aggregate initialization in init lists is not available, so you have to use workarounds, e.g.:
myClass.cpp
#include "myClass.hpp"
//Initialize structure in Constrcutor
myClass::myClass( )
: bar() //initialization with 0
{
const static foo barInit = {1, 0, "someString", 0x4}; //assignment
bar = barInit;
}
Or:
#include "myClass.hpp"
namespace {
foo const& initFoo() {
const static foo f = {1, 0, "someString", 0x4};
return f;
}
}
//Initialize structure in Constrcutor
myClass::myClass( )
: bar(initFoo()) //initialization
{ }
Upvotes: 11
Reputation: 3369
Initialization should be done this way (C++11):
myClass::myClass( )
: bar{1, 0, "someString", 0x4}
{
}
Also, do not forget to declare your constructor in your class definition.
Upvotes: 7