Solved

C++ inheritance with pointers to array

Posted on 2013-02-04
19
373 Views
Last Modified: 2013-02-05
I have a main class and there are going to be many sub classes of it.  In each sub class, I would know certain fixed arrays that would provide a means for making certain calculations.  The answer is provide to the main function's array.  I could go by not doing sub classes, but only declaring instances of the parent class based on many sets of array values but I want to go more in the object oriented way.  

Below, I have made a much simpler scenario of the actual problem but I get compilation errors.  I need to understand how to resolve it and why it is happening.

Thank you.

#include<iostream>
#include<string>

using namespace std;

const int  num = 10;

const string vals[num] = {"first", "second", "third", "fourth", "Fifth", "6th", "7th", "8th", "9th", "10th"};

class A
{
public:
	A(string* s1, string* s2)
	{
		v1 = s1;
		v2 = s2;
	}
	void copy()
	{
		for(int i=0; i < num; i++)
		{
			v2[i] = v1[1];
		}
	}
protected:
	string* v1;
	string* v2;
};

class B : public A
{
public:
	B(string* s)
	{
		A(vals, s);
	}
};


int main()
{
	string ans[10];
	B b(ans);
	b.copy();
	for(int i=0; i < num; i++)
	{
		cout << ans[i] << endl;
	}
	cin.get();

	return 0;
}

Open in new window

0
Comment
Question by:farzanj
  • 10
  • 4
  • 3
  • +1
19 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 38851840
One problem is the way you are calling A's constructor from B's constructor, that should be like

class B : public A
{
public:
	B(string* s) : A(vals, s) // call base class constructor from initializer list
	{
		
	}
};

Open in new window


Then 'const' is a problem for that call - just make the array not const to avoid that, e.g.

#include<iostream>
#include<string>

using namespace std;

const int  num = 10;

string vals[num] = {"first", "second", "third", "fourth", "Fifth", "6th", "7th", "8th", "9th", "10th"};

class A
{
public:
	A(string* s1, string* s2)
	{
		v1 = s1;
		v2 = s2;
	}
	void copy()
	{
		for(int i=0; i < num; i++)
		{
			v2[i] = v1[1];
		}
	}
protected:
	string* v1;
	string* v2;
};

class B : public A
{
public:
	B(string* s) : A(vals, s) // call base class constructor from initializer list
	{
		
	}
};


int main()
{
	string ans[10];
	B b(ans);
	b.copy();
	for(int i=0; i < num; i++)
	{
		cout << ans[i] << endl;
	}
	cin.get();

	return 0;
}
                                  

Open in new window

0
 
LVL 31

Author Comment

by:farzanj
ID: 38852013
Suppose I needed to declare it a constant, what would I do?
Suppose I needed to call the super class' constructor within the function (as I recall, I used to do it) what would I do?  A::A()?

Slightly making the above example more realistic.

#include<iostream>
#include<string>

using namespace std;

const int  num = 10;

string vals[num] = {"first", "second", "third", "fourth", "Fifth", "6th", "7th", "8th", "9th", "10th"};

class A
{
public:
	A(string* s1, string* s2, const int& n)
	{
		v1 = s1;
		v2 = s2;
	}
	void copy()
	{
		for(int i=0; i < n; i++)
		{
			v2[i] = v1[i];
		}
	}
protected:
	int&    n;
	string* v1;
	string* v2;
};

class B : public A
{
public:
	B(string* s, int& n) : A(vals, s, n) // call base class constructor from initializer list
	{
		
	}
};


int main()
{
	string ans[10];
	B b(ans, num);
	b.copy();
	for(int i=0; i < num; i++)
	{
		cout << ans[i] << endl;
	}
	cin.get();

	return 0;
}

Open in new window

0
 
LVL 31

Author Comment

by:farzanj
ID: 38852039
Also, suppose I want to declare
const string vals[num] = {"first", "second", "third", "fourth", "Fifth", "6th", "7th", "8th", "9th", "10th"};

Open in new window

as

const char*  vals[num] = {"first", "second", "third", "fourth", "Fifth", "6th", "7th", "8th", "9th", "10th"};

Open in new window


With no more changes, how would I do it?
0
 
LVL 86

Expert Comment

by:jkr
ID: 38852220
OK, step by step - if you have references as class members, they need to be initialized in the initializer list as well:

#include<iostream>
#include<string>

using namespace std;

const int  num = 10;

string vals[num] = {"first", "second", "third", "fourth", "Fifth", "6th", "7th", "8th", "9th", "10th"};

class A
{
public:
	A(string* s1, string* s2, const int& n1) : n(n1)
	{
		v1 = s1;
		v2 = s2;
	}
	void copy()
	{
		for(int i=0; i < n; i++)
		{
			v2[i] = v1[i];
		}
	}
protected:
	const int&    n;
	string* v1;
	string* v2;
};

class B : public A
{
public:
	B(string* s, int& n) : A(vals, s, n) // call base class constructor from initializer list
	{
		
	}
};


int main()
{
	string ans[10];
        int n1 = num;
	B b(ans, n1);
	b.copy();
	for(int i=0; i < num; i++)
	{
		cout << ans[i] << endl;
	}
	cin.get();

	return 0;
}
                                            

Open in new window

0
 
LVL 86

Assisted Solution

by:jkr
jkr earned 166 total points
ID: 38852229
OK, using a 'const char* []' does not seem to be that simple in terms of conversions - is there a reason why you would need that, especially when the classes are declared to use a 'std::string'??
0
 
LVL 31

Author Comment

by:farzanj
ID: 38852324
What is the difference between

      A(string* s1, string* s2, const int& n1)// : n(n1)
      {
            n  = n1;
            v1 = s1;
            v2 = s2;
      }

Compile time vs. run time?  So why the first one works and the other one doesn't?
0
 
LVL 31

Author Comment

by:farzanj
ID: 38852346
For all the questions that I am asking, I am trying to regain my c++ skills that I used to have in last 90's.

const char* vs. const string.

I have an impression that declaring global const strings is not a preferred practice and it is better to do const char*.  But if it is a fine practice, still I would like to know for my knowledge.

Also, how would you compile the code if
string vals[num]

was const?

const string val[num]

Can I use const_cast<> or any other mechanism?
0
 
LVL 31

Author Comment

by:farzanj
ID: 38852353
Again I could be wrong but I remember in 90's calling super class' constructor by doing something like

A::A()

But I get an error message when I try to do it.  So, is there any notation similar to it?
0
 
LVL 86

Assisted Solution

by:jkr
jkr earned 166 total points
ID: 38852420
Well, the reason is that 'n' is an 'int&' class member, and references *have* to be initialized in the initializer listbecause of their sheer nature. You could do what you described with a pointer, yet not with a reference.

Regarding the string array vs. 'char*' array issue, the compiler can easily convers a single 'char*' to a string on the fly, even with more than one argument - yet it cannot construct a string array on the fly, that you'd have to do manually. And in that case, you could as well just stick with a string array or consequently use 'char*' in the 1st place.
0
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 
LVL 32

Assisted Solution

by:sarabande
sarabande earned 83 total points
ID: 38854132
all of your issues arise cause you used string pointers, global variables and c arrays.

you should use string references inted of pointers which easily can made const and can take const char * for initializing. you should use std::vector<std::string> for string arrays and you you should use static members instead of global variables.

applying all this to your classes you get

#include<iostream>
#include<string>
#include<vector>
#include <algorithm>
using namespace std;

enum
{
   first = 0, second, third, fourth, fifth, sixth, seventh, eighth, nineth, tenth, max_num 
};


class A
{
    static const char * vals[max_num];
public:
    A()
    {
    }
    void copy(std::vector<std::string>& s2)
    {
        std::copy(&vals[0], &vals[max_num], s2.begin());
    }
protected:
};

const char * A::vals[max_num] = {"first", "second", "third", "fourth", "Fifth", "6th", "7th", "8th", "9th", "10th", };

class B : public A
{
public:
    // no constructor needed
};


int main()
{
    std::vector<std::string> ans(10);
    B b;
    b.copy(ans);
    for(int i=0; i < num; i++)
    {
        cout << ans[i] << endl;
    }
    cin.get();

    return 0;
}

Open in new window


Sara
0
 
LVL 22

Accepted Solution

by:
ambience earned 251 total points
ID: 38854457
>> Suppose I needed to call the super class' constructor within the function (as I recall, I used to do it) what would I do?  A::A()?
>> But I get an error message when I try to do it.  So, is there any notation similar to it?

This.  

class A
{
protected:
      A(){}

public:
      A(string* s1, string* s2)
...
};

class B : public A
{
public:
      B(string* s, const int& n)
      {
            A(s, vals);
                A::A(s,vals);
            this->A::A(s, (string*) vals);
      }
};

The problem you had was that there wasnt any default constructor in A.

>> Also, suppose I want to declare
const char*  vals[num] = {"first", "second", "third", "fourth", "Fifth", "6th", "7th", "8th", "9th", "10th"};
           
I dont understand what you mean by "With no more changes"  

string* is not implicitly convertible to char** so without "more changes" its impossible!
                             
Also, from what I can understand, A's purpose is to copy arrays of objects. If you add const then you totally change the purpose of A.

>> I have an impression that declaring global const strings is not a preferred practice and it is better to do const char*.  But if it is a fine practice, still I would like to know for my knowledge.

The answer is - it depends.

Almost all std::string implementations implement copy-on-write and short-string-optimizations and that means if you have a lot of code copying strings from the global array its going to be pretty fast - not to mention references that are as fast as pointers. using a char* as global would require strlen whenever you need to find the length. With string you just use the size(). Additionally, std::string is more type-safe and leads to type safe code.

The downsides.

For one, it would take more space because of the internal data. It would also require some heap allocations (depending on the implementation). BUT probably the biggest gotcha is that constructors for global objects are not executed in a deterministic order - so if some other global object constructor use the global array of string, it may get uninitialized version of it. There is a simple workaround for that

const string* gArrayOfString() {
      static string array[] ...........; return array;
}

Using gArrayOfString() would ensure the array is always constructed no matter where its used - even in global object constructors. However, this looses the safety of array bounds and you'd need to find out the length of the array somehow. The best bet is to use a global vector of string

const vector<string>& gArrayOfString() ...

>> Also, how would you compile the code if
string vals[num] was const?

const string val[num]
B b(const_cast<string*>(vals), num);

not a good practice!
0
 
LVL 31

Author Comment

by:farzanj
ID: 38854722
All, thank you for your input.  As I said earlier, it is a much too simplified version I made just to ask a question that would be answered.  If I post my whole long solution, no one would be able to read the whole thing.

Sara: I used C arrays (they were considered c++ until late 90's when I used to do it) because I am writing a fast execution parser that processes TBs of data every day. So I want to use stacks instead of heap.  I didn't want global variables as I originally posted.  It was constant globals and with constants globals were considered fine, I don't know about now.  However, I can make them static member.  Class 'A' is my general parser, class 'B' would initialize tokens in class A and also over ride the pure virtual functions in class A.  So, I believe, I do need constructor for B.  And there would be many classes instead of one B because the general parser can parse any type of data depending upon tokens and inputs.  You took my example literally so you changed it completely.  I would like to pass references of static member string array from B to A if I could compile it :)
0
 
LVL 31

Author Comment

by:farzanj
ID: 38854767
ambience: Your answers are closest to what I wanted to ask, so far.
So, if I do not declare a default constructor, is it always protected? or private?
In general without declaring an access type what is default access type, protected or private?

I do not want to use global vector because I am still under the old notion that an array on the stack will be faster and I want as much speed as I can get.

I don't need char* array either because I need lengths of my tokens to be very handy as calculations will be based on them.

>>Also, from what I can understand, A's purpose is to copy arrays of objects. If you add const then you totally change the purpose of A.

No, as I explained above, it is just an example and I made it do something that would pass variables around so that I could get the syntax help.

My single class parser simply works great, I would use it directly by eliminating pure virtual function but parser has many different types and for each one I wanted to customize based on tokens and kind of output required using the object oriented way.
0
 
LVL 22

Assisted Solution

by:ambience
ambience earned 251 total points
ID: 38854843
>> So, if I do not declare a default constructor, is it always protected? or private?

If you supply a non-default constructor then there is no implicit default constructor (the default constructor is deleted - as per the new c++ standard). You must provide an implementation if you need it OR in the latest standards you can undelete it like

class A
{
A(int i); // non-default ctor
A() =  default; // ask compiler to generate default ctor
A(const A&) = default; // ask compiler to generate default copy ctor

...

If you do not supply any contructor in the class then the behavior is as if these were defined implicitly.

class A {
A() = default;
A(const A& ) = default;
.....

>> In general without declaring an access type what is default access type, protected or private?

private for class, public for struct
0
 
LVL 31

Author Comment

by:farzanj
ID: 38854979
So in my sub class, I want to declare tokens to be used by the parent class (A) so I don't mind if it is in a namespace or a global constant array or a static member variable of the subclass B but it should not be static in the parent (A) because parent will be used by multiple class and I don't want all instances of those to have the same token data.

Also, please feel free to comment on my use of const string array instead of vector due to speed or my design as I am trying to do the fastest design with best practices.

Appreciate your time and help.
0
 
LVL 22

Assisted Solution

by:ambience
ambience earned 251 total points
ID: 38855076
>> I want to declare tokens to be used by the parent class (A)

Perhaps its better to keep a pointer to token array in A (by default pointing to A's token array) but each subclass can set the pointer to a different token array in the ctor? I dont know if that even makes sense.

>> please feel free to comment on my use of const string array instead of vector due to speed or my design as I am trying to do the fastest design with best practices.

As far as the performance goes, only real benchmarks can tell you the difference - if theres any.

Theoretically, raw array access can be the faster (but as I mentioned it depends on how the rest of the code uses the array). For example, for a string array you need at least two levels  of indirection to reach the real buffer - first accessing the internal stringbuffer and then the pointer in it to the real chunk (most implementations are like that).

BUT .. how much faster? Unless a benchmark confirms the opposite I would say that a string array would be just as fast as a char* array.
0
 
LVL 31

Author Closing Comment

by:farzanj
ID: 38855488
Thank you all.  I will have follow up of this discussion.
0
 
LVL 32

Expert Comment

by:sarabande
ID: 38855495
your current design is that baseclass A gets two string arrays. the derived class B passes one global array and one array that was passed as an argument to B's constructor. the first array was individual for class B. if you would derive another class from A it could pass a different array. so my approach to make the first array a ststic member of A was wrong if you would enhance the sample code. it should be a static member of B instead.

class A
{
    std::vector<std::string> v1;
public:
    A(const std::vector<std::string> & s1) 
    {
          v1 = s1;   // v1 is now a copy of s1
    }
    void copy(std::vector<std::string>& s2)
    {
          s2 = v1;
    }
};

enum { num_vals = 10 };

class B : public A
{
       static const char * vals[num_vals];
public:
       B() : A(std::vector<std::string>(&vals[0], &vals[num_vals])) { } 
};

// static initialisation
const char * B::vals[num_vals] = {"first", "second", "third", "fourth", "Fifth", "6th", "7th", "8th", "9th", "10th", };

Open in new window


note the vals is an array of const char * because that allows static initialization (what is quite handy).

nevertheless you could initialize the vector member of A by use an appropriate constructor of std::vector.

the std::vector internally provides/uses a c array of the template type what in your case is an array of strings. so the difference of the performance of std::vector vs. c arrays is only in the overhead of calling a function instead of directly compiled code what is non-measurable even for big arrays.

but it has a lot advantages, for example it could have different number of elements depending on derived classes, what probably is a functionality you need.

note, neither A nor B should store a reference or pointer to the second array. it is much better design to pass the array with the copy function than to pass the array in the constructor and save it as a member. generally, class members should be encapsulated in the class. the variables hould not be created outside of the class nor should they made direct accessible from outside.

Sara
0
 
LVL 31

Author Comment

by:farzanj
ID: 38855689
Hi Sara,

Sorry, I had already closed this discussion.  It continues here:

http://www.experts-exchange.com/Programming/Languages/CPP/Q_28020557.html

I will reply to your comment there.
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Suggested Solutions

Navigation is an important part of web design from a usability perspective. But it is often a pain when it comes to a developer’s perspective. By navigation, it often means menuing. This is less theory and more practical of how to get a specific gro…
Windows Script Host (WSH) has been part of Windows since Windows NT4. Windows Script Host provides architecture for building dynamic scripts that consist of a core object model, scripting hosts, and scripting engines. The key components of Window…
This video teaches viewers about errors in exception handling.
This tutorial explains how to use the VisualVM tool for the Java platform application. This video goes into detail on the Threads, Sampler, and Profiler tabs.

706 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now