c++ - design and initialization of data in derived classes -


i have following hierarchy:

  base_class       |  traits_class       | concrete_class 

now thing data contained in base_class (it needs there because traits_class has have access it. traits_class class template has different functionality depending on template parameter passed (so use partial template specialization different classes). finally, @ lowest level, concrete_class class template. create instances of concrete_class only.

now question is: have written constructors, destructor , have provided move semantics within concrete_class. means not call base constructors, initialize state directly in derived classes. can point out if there problem this? destructor declared in base_class, , declared protected. there evident flaw in design?

thanks insight!

edit

so revised design following yakk's comment on crtp, , have

 traits_class       | concrete_class 

i have moved data concrete_class, , crtp can have access in traits_class. weird happened though, couldn't access data in traits_class within constructor of traits_class. mean, did access it, seemed if accessing ghost data, because initialized members in traits_class (and printed within traits_class constructor), afterwards class empty. don't understand happened (i const_casting traits_class concrete_class this).

in end, wrote static member functions in traits_class initialize members of concrete_class. guess have used protected member functions same thing (because i'm inheriting traits_class), believe it's same thing.

if have further comments, please let me know. , again c++ wisdom.

aa

there error in reasoning. constructor initializes base classes , nonstatic members (technically, virtual base initialized most-derived type , not of other bases' constructors), base_class initialized compiler-generated default constructor (or compiler-generated copy/move constructors if you're doing copy or move), initialize data members using default (or copy/move) constructors. may later assign members in concrete class's constructors, initialization has occurred point.

since base class owns data members, base class initializes data members when copy or move occurs. if write own copy or move constructor in most-derived class, you'll either need invoke base class's copy/move constructor in initialization list or data members default-constructed , you'll forced use copy/move-assignment after fact. inefficient , in cases may incorrect. (for example, i've written classes move-constructed, not move-assigned because of slicing issues; if had such class in base_class data member, couldn't implement move semantics solely in concrete_class.)

if must initialize data members concrete_class, recommend provide protected constructor in base_class takes data members value , moves them own data members, , provide perfect-forwarding constructor in traits_class (or inherit base's constructor, if using compiler support). allows concrete class specify values initializing data members, allows base class actual initialization. allows base_class , traits_class constructors access initialized data members (whereas otherwise can access data members in default-initialized state).

here's example:

 struct base_class { protected:     base_class( string s ) : s_( move(s) ) { }     ~base_class() = default;     // request copy/move, since we're declaring our own (protected) destructor:     base_class(base_class const &) = default;     base_class(base_class &&) = default;     base_class &operator=(base_class const &) = default;     base_class &operator=(base_class &&) = default;           string s_; };   template<int> struct traits_class : base_class { protected:     template<typename... p>     traits_class( p &&p )         : base_class( forward<p>(p)... )     { } };   template<int i> struct concrete_class : traits_class<i> {     concrete_class( char c )         : traits_class<i>( string( i, c ) )     { } }; 

as side-note, data doesn't have in base_class traits_class able access it. provide access via protected virtual function if don't mind overhead of virtual function call , if don't need access within constructors or destructor (which i'm assuming don't, since data members don't have final values until after concrete_class's constructor runs). or, avoid virtual call overhead, use curiously recurring template pattern @yakk mentions.

== response edit in original question ==

the base class's constructor run before derived class's constructor, if data stored in derived class, uninitialized in base class's constructor (and reclaimed in destructor). can think of constructor taking instance of base class , turning instance of derived class (by initializing derived part of class, etc.), , special case, constructor class no base classes turns "nothing" (raw storage) instance of class.

so, when traits class constructor running, isn't yet concrete derived class. consequently, accessing derived class's data members or otherwise using class derived class illegal. language enforces virtual functions; if call virtual function inside base class's constructor or destructor, call base version of function. example:

#include <iostream> using namespace std;  struct base {   base() { cout << "base ctor\n"; v(); }   ~base() { v(); cout << "base dtor\n"; } protected:   virtual void v() const { cout << "base::v\n"; } };  struct derived : base {   derived() { cout << "derived ctor\n"; v(); }   ~derived() { v(); cout << "derived dtor\n"; } protected:   virtual void v() const { cout << "derived::v\n"; } };  int main() { derived d; }  /* output: base ctor base::v derived ctor derived::v derived::v derived dtor base::v base dtor */ 

Comments

Popular posts from this blog

monitor web browser programmatically in Android? -

Shrink a YouTube video to responsive width -

wpf - PdfWriter.GetInstance throws System.NullReferenceException -