placing constructor in private

Started by
8 comments, last by Bregma 10 years, 4 months ago

I am currently studying the design patterns described by the Gang of Four. The Composite Pattern, is what I am currently trying to concertize mentally. While doing this I was looking at an example of its implementation shown here. If you notice in the portion were they are implementing the leaf classes of this pattern they place a generic version of the classes" constructor in private and they place an overloaded version of the constructor in public. Trying to figure this out on my own I came across a forum post that describes the various reasons one might place the constructor in private here by Kerido. Of those 5 examples I am certain its not B or E. But out of A,C, and D I am not sure. I place portions of the code I am talking about to reduce having to hunt through the posted link above. Also the explanations of A, C, and D (reasons to have a constructor in private) I'll past below the class in question.


/*
 * Capacitor is a LEAF class which is directly inherited from the -
 * ElectronicComponet class.
 */
class Capacitor:public ElectronicComponent
{
	float capacitance_muF; //Capacitance value in micro fared.

	Capacitor(){} // Point in question. Why are they doing this, in this context?
public:
	/*
	 * Constructor - initialize the capacitance value.
	 */
	Capacitor(float muF)
	:capacitance_muF(muF)
	{

	}
	/*
	 * Prints the Capacitor's specification.
	 */
	void PrintSpec(ostream& ostr, string prefix = "")
	{
		ostr<<prefix<<"Capacitor ("<< capacitance_muF <<" muF )"<<endl;
	}
	/*
	 * Performs the Capacitor's real function of modifying  input Voltage & Current.
	 * Electrical engineers, please help :)
	 * Now I just put a printing of Voltage and current as place holder.
	 */
	void DoFunction(float& voltage, float& current)
	{
		cout<<endl<<"Capacitor Input ("<<voltage<<" V ,"<<current<<" Amp) 
			Capacitance ="<< capacitance_muF<<endl;
	}
};

A. Your class instances are created in a static method. The static method is then declared as public.


class MyClass()
{
private:
  MyClass() { }

public:
  static MyClass * CreateInstance() { return new MyClass(); }
};

C. (Only applies to the upcoming C++0x standard) You have several constructors. Some of them are declared public, others private. For reducing code size, public constructors 'call' private constructors which in turn do all the work. Your public constructors are thus called delegating constructors:


class MyClass
{
public:
  MyClass() : MyClass(2010, 1, 1) { }

private:
  MyClass(int theYear, int theMonth, int theDay) { /* do real work */ }
};

D. You want to limit object copying (for example, because of using a shared resource):


class MyClass
{
  SharedResource * myResource;

private:
  MyClass(const MyClass & theOriginal) { }
};

Any thoughts on the matter would be nice thanks

J-GREEN

Greenpanoply
Advertisement

I would say it is to force the user to initialize the class with an explicit value, since there is no default value that makes sense.

So it has nothing to do with the pattern, but with what the pattern in this case models, which is an electronic circuit, and all resistors and capacitors need to have a value specified.

You could just not define it though... since another constructor is defined the compiler wouldn't provide a default constructor automatically, so I see no purpose for it?

Not having a default constructor means you can't create them with operator new[] (may not apply in C++11, I'd have to check) and there may problems using them with standard library containers, I believe, since they reserve space with default constructed objects?

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

Olof Hedman

Olof Hedman: Your explanation make perfect sense. But to be clear you are saying:

Had the default constructor been placed in public, the client then could declare an instance of that class with out passing it values, and then immediately try to use it, causing problems?

Paradigm Shifter: are you saying to have the constructor in the public in this fashion is useless because the compiler would supply the default constructor regardless if parameters were not passed?

J-GREEN

Greenpanoply


Had the default constructor been placed in public, the client then could declare an instance of that class with out passing it values, and then immediately try to use it, causing problems?

Yes.


You could just not define it though... since another constructor is defined the compiler wouldn't provide a default constructor automatically, so I see no purpose for it?

Oops, that is true... temporarily forgot about that for some reason :)

He probably just got a bit overzealous when writing the code.

Not having a default constructor does have a purpose though.


Not having a default constructor means you can't create them with operator new[] (may not apply in C++11, I'd have to check) and there may problems using them with standard library containers, I believe, since they reserve space with default constructed objects?

It will work with most containers, as long as you don't try to reserve space in them. push_back etc will work. (c++11 std::array will not work)

But I'd say that is fine, because doing that would violate the invariant that these objects should never be allowed to be created with a "default" value (since there is no default value that makes sense)

huh.png lol so then it is to prevent un-initialized values that would cause a violation of the invariant of this particular situation?

I am not sure if this helps but here is the example of usage by the author of said instance. I mean just to clarify and perhaps concertize an answer.


int main()
{
	Resistor r1(50), r2(70); //Define Resistors
	Capacitor c1(200),c2(300); //Define Capacitors

	ICChip ic1; //Create a Chip
	ic1.AddChild(new Resistor(2000)); // Add a Resistor inside the ICChip
	ic1.AddChild(new Capacitor(1000)); // Add a Capacitor inside the ICChip

	PCB pcb1; //Make  PCB Object and add the Resistor, Capacitors and ICChip on it
	pcb1.AddChild(&r1);
	pcb1.AddChild(&c1);
	pcb1.AddChild(&c2);
	pcb1.AddChild(&ic1);
	pcb1.AddChild(&ic1); // Duplicate child entries are ignored.

	cout<<"\n=========== Printing the PCB Spec =========="<<endl;
	pcb1.PrintSpec(cout);
	float v =110, i = 5;
	cout<<"\n=========== DoFunction(110,5) of PCB =========="<<endl;
	pcb1.DoFunction(v,i);
	cout<<"\n=========== Removing c2 from PCB =========="<<endl;
	pcb1.RemoveChild(&c2);
	cout<<"\n=========== Printing the PCB Spec =========="<<endl;
	pcb1.PrintSpec(cout);
	
	return 0;
} 
J-GREEN

Greenpanoply

Ok, so I created a program using these classes. I then ran it twice. Once with said constructor of the resistor class (Leaf) in public and ones with it in private. during both runs I only declared the resistor object with out initializing it.

When I ran the program while the constructor was in private and I did not initialize that object the compiler threw an error:

Error 1 error C2248: 'Resistor::Resistor' : cannot access private member declared in class 'Resistor'

When I ran the program while the constructor was in public and I did not initialize that object it did not throw an error. However it did include garbage values that worked it's way all through the program causing issues as far as how the system is suppose to work only giving proper values of Ohm or muF

So in conclusion: I do believe Olof Hedman original thoughts on the matter were correct, concerning the author forcing the client to initialize the object through its constructor by placing the "default constructor" in private.

Thanks for the help you guys biggrin.png

J-GREEN

Greenpanoply

If you remove the private constructor completely you'll get a compile error too if you try to construct the object with no constructor arguments. The compiler does not provide a default constructor for you if you provide at least one constructor.

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

You are completely right. So I guess preference is what compiler error do you want the client to run into, when failing to pass arguments to the constructor?

//with private default constructor

Error 1 error C2248: 'Resistor::Resistor' : cannot access private member declared in class 'Resistor'

or

//without default constructor

Error 1 error C2512: 'Resistor' : no appropriate default constructor available

J-GREEN

Greenpanoply

guess preference is what compiler error do you want the client to run into, when failing to pass arguments to the constructor?

Well, there's more to it than that. Assuming your code all compiles cleanly because you've done the right thing™ everywhere, if you have an inaccessible private constructor you end up with an additional object with external linkage that has to be resolved by the linker or the runtime link-loader. Repeat for a few classes and you get larger executables that take longer to build and to start. You also have more code to try to read during maintenance, and remember all code is in maintenance once it's been written. There's always fewer bugs in code you never write.

I would take the 'no appropriate default constructor available' compile-time message any time.

Stephen M. Webb
Professional Free Software Developer

This topic is closed to new replies.

Advertisement