Correct way of initializing a struct in a class constructor

前端 未结 5 599
灰色年华
灰色年华 2020-12-08 02:44

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 thi

相关标签:
5条回答
  • 2020-12-08 03:00

    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
    };
    

    C++11:

    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.

    C++03:

    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
    { }
    
    0 讨论(0)
  • 2020-12-08 03:08

    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;
    }
    
    0 讨论(0)
  • 2020-12-08 03:12

    C++03 Style

    #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
    }
    

    C++11 Style

    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.

    0 讨论(0)
  • 2020-12-08 03:13

    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. :)

    0 讨论(0)
  • 2020-12-08 03:21

    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.

    0 讨论(0)
提交回复
热议问题