May 02, 2024, 09:00:59 PM

News:

Own IWBasic 2.x ? -----> Get your free upgrade to 3.x now.........


Class constructors

Started by Parker, December 08, 2005, 04:28:49 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Parker

Will it eventually be possible to do something like this:
Object obj(2);
or
Object *obj;
obj = new Object(2);

where either would call the constructor
Object::Object(int value)
{
...
return;
}

Or some variation, I realize it may not be the same, but will it be possible?

Protected

Are constructors implemented yet?

Mike Stefanik

May 19, 2006, 09:48:16 AM #2 Last Edit: May 19, 2006, 09:49:49 AM by Mike Stefanik
Right now Aurora only supports default class constructors (i.e.: a constructor with no arguments). To be able to support arguments, the first thing that Paul would need to do is modify the compiler to support method overloading; then support for passing arguments to constructors should be easy to implement. It's something that he's said that he's going to do, but I'm not sure where it falls on the list of priorities.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Protected

Oh, ok. I was wondering because I saw a constructor in the imgview example and they'd be useful to me  :) Thanks for the clarification.

Parker

I don't know that it'd be particularly hard just to get overloading functions, but the problems arise with the current coersion where you can pass an integer variable where it expects a pointer and not get an error. Anything that can is automatically converted, and we'd lose a ton of that to have overloaded functions. Also there is currently no casting, except for the hackish *(sometype)(&x) which may or may not work since I haven't tried it, so you couldn't force it to call SomeFn(byte) instead of SomeFn(int) like you can in C++.

Mike Stefanik

May 19, 2006, 04:45:12 PM #5 Last Edit: May 19, 2006, 05:03:08 PM by Mike Stefanik
The solution, in my opinion, would be to add an "overload" keyword. For example:


class MyClass
{
ÂÃ,  ÂÃ,  declare overload MyFunction(int nParam);
ÂÃ,  ÂÃ,  declare overload MyFunction(int *pnParam);
ÂÃ,  ÂÃ,  declare overload MyFunction(byte nParam);
}


If you call a function that's been tagged as an 'overload' then the parameters have to match one of those signatures exactly and there's no type promotion performed. Without the keyword, then it's handled as it is now. It would allow for overloading, with backwards compatibility for existing code.

Edit: The one thing that Paul would have to implement to make this all work correctly would be typed pointers. Right now, every pointer is essentially treated as a void pointer. He'd need to add type checking so that this kind of thing would work:


class MyClass
{
    declare overload MyFunction(MYSTRUCT1 *pStruct1);
    declare overload MyFunction(MYSTRUCT2 *pStruct2);
}


With untyped pointers, there'd be no way for the compiler to know which function should be called, even with an overload keyword.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

John S

May 20, 2006, 10:49:12 PM #6 Last Edit: May 20, 2006, 11:24:44 PM by John S
I've been working on my tutorial again and ran into an issue discussing Classes.   My first example, "class1-0.src" works fine.

I then wrote "class1-1.src" and ran into trouble with my child class.  It seemed to inherit o.k. but when I defined (redefined) a couple of methods, the new child methods were not called when I declared the child object.  I had to create a new constructor for the child to get it all to work.  This means the parent constructor and the child constructor are called/used.  That can be a little costly in processing time if and when I ever get to coding my Matrix libraries.

  ( edit:  I guess this is the way it has to be until Overloading Methods becomes possible )

Do I have to explicitly call the destructors?

I attached the .src and .exe files


// Dog.inc
// This include file provides the Dog Class

class Dog
{
  def Tails, Legs        as int;
  def Color, Name, Ears  as string;

  declare  Dog();                       // Dog constructor
  declare _Dog();                       // Dog destructor

  declare giveName( string somename );
  declare growTail();
  declare growLegs();
  declare firColor();
  declare growEars();

  declare OutputToScreen();
}

Dog::Dog()
{
  writeln("\nDog constructor has been called\n\n");
  growTail();
  growLegs();
  growEars();
  firColor();
  return;
}

Dog::_Dog()
{
  writeln("Dog destructor has been called\n\n");
  return;
}

Dog::growTail()
{
  writeln("Dog grows a tail\n");
  Tails = 1;
  return;
}

Dog::growLegs()
{
  writeln("Dog grows legs\n");
  Legs = 4;
  return;
}

Dog::firColor()
{
  writeln("Dog grows fir\n");
  Color = "brown";
  return;
}

Dog::giveName( somename as string )
{
  writeln("your Dog is named\n");
  Name = somename;
  return;
}

Dog::growEars()
{
  writeln("Dog grows ears\n");
  Ears = "pointed";
  return;
}

Dog::OutPutToScreen()
{
  writeln ("\n\n");
  writeln ("\n     the name of my dog is " + Name);
  writeln ("\n     my dog has " + str$(Tails) + " tails");
  writeln ("\n     my dog has " + str$(Legs) + " legs");
  writeln ("\n     my dog's fir is " + Color);
  writeln ("\n     my dog has " + Ears + " ears");
  writeln ("\n\n\n");
}




/*  Dog.src - to be compiled as a console executable  */

#include "Dog.inc"

global sub main
{
  Dog Spot;
  Spot.OutPutToScreen();

  Spot._Dog();

  writeln ("\npress any key\n");
  while (GetKey() == "");
  return;
}




/*  BlackLabrador.src - to be compiled as a console executable  */

#include "Dog.inc"

class BlackLabrador : Dog
{
  declare  BlackLabrador();                       // BlackLabrador constructor

  declare firColor();
  declare growEars();
}

BlackLabrador::BlackLabrador()
{
  writeln("\nBlackLabrador constructor has been called\n\n");
  growEars();
  firColor();
  return;
}

BlackLabrador::firColor()
{
  writeln("Black Labrador grows fir\n");
  Color = "black";
  return;
}

BlackLabrador::growEars()
{
  writeln("Black Labrador grows ears\n");
  Ears = "floppy";
  return;
}


global sub main
{
  Dog Spot;
  Spot.giveName("Spot");

  BlackLabrador Skippy;
  Skippy.giveName("Skippy");

  Spot.OutPutToScreen();
  Skippy.OutPutToScreen();

  Spot._Dog();
  Skippy._Dog();

  writeln ("\npress any key\n");
  while (GetKey() == "");
  return;
}
John Siino, Advanced Engineering Services and Software

Ionic Wind Support Team

You forgot the VIRTUAL keyword.  Only virtual methods are overridable. 

You never explicitly call a constructor or destructor.  That is handled by the compiler automatically.
Ionic Wind Support Team

John S

John Siino, Advanced Engineering Services and Software

Ionic Wind Support Team

And if you want to see a destructor in action use NEW and DELETE to dynamically allocated the class.

When you define a class in main() the destructor is called when main ends.  So you will not see output from a PRINT statement in the destructor.

global submain()
{
SomeClass class;  //constructor (including base classes) called here
while getkey() = "";
} //destructor (including base classes) called here

If you use a pointer and NEW and DELETE you can see your test output

global submain()
{
Dog *spot = new(Dog,1);  //constructor (including base classes) called here
delete dog;  //destructor (including base classes) called here
while getkey() = "";
}
Ionic Wind Support Team

John S

May 21, 2006, 12:12:52 AM #10 Last Edit: May 21, 2006, 12:26:12 AM by John S
Thanks again Paul.   When do you expect to be able to allow multiple layers of inheritance (BlackLab from  Retriever from Dog)

I revamped the coding and renamed it MyDog.src.  I editted my post to Larry at:

http://www.ionicwind.com/forums/index.php?topic=611.msg5397#msg5397
John Siino, Advanced Engineering Services and Software

Mike Stefanik

May 21, 2006, 12:51:12 AM #11 Last Edit: May 21, 2006, 12:54:43 AM by Mike Stefanik
As a suggestion, when you declare class member variables, prefix them with "m_" to make it easy to distinguish in code that you're talking about the member of a class, and not a local variable. For example, I'd implement your class as:


class Dog
{
ÂÃ,  int m_nTails;
ÂÃ,  int m_nLegs;
ÂÃ,  string m_strColor;
ÂÃ,  string m_strName;
ÂÃ,  string m_strEars;

ÂÃ,  declareÂÃ,  Dog();ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, // Dog constructor
ÂÃ,  declare _Dog();ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, // Dog destructor

ÂÃ,  declare giveName( string somename );
ÂÃ,  declare growTail();
ÂÃ,  declare growLegs();
ÂÃ,  declare firColor();
ÂÃ,  declare growEars();

ÂÃ,  declare OutputToScreen();
}


The other suggestion is that instead of referencing the members directly, you create accessor methods:


CDog::GetLegs(), int
{
ÂÃ,  ÂÃ,  return m_nLegs;
}

CDog::SetLegs(int nLegs)
{
ÂÃ,  ÂÃ,  m_nLegs = nLegs;
}


What this allows you to do is improve the interface by performing checks on inputs, such as:


CDog::SetLegs(int nLegs), int
{
ÂÃ,  ÂÃ,  if (nLegs < 1 || nLegs > 4)
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  return false;

ÂÃ,  ÂÃ,  m_nLegs = nLegs;
ÂÃ,  ÂÃ,  return true;
}


Finally, it allows you to transparently modify your code so that you can integrate new features and tie them to the accessor methods. For example, let's say that you wanted to add a flag if the dog has been injured. You add the member variable m_bInjured to the class and then can modify SetLegs() as:


CDog::SetLegs(int nLegs), int
{
ÂÃ,  ÂÃ,  if (nLegs < 1 || nLegs > 4)
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  return false;

ÂÃ,  ÂÃ,  if (nLegs !=ÂÃ,  4)
ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  m_bInjured = true;

ÂÃ,  ÂÃ,  m_nLegs = nLegs;
ÂÃ,  ÂÃ,  return true;
}

CDog::IsInjured(), int
{
ÂÃ,  ÂÃ,  return m_bInjured;
}

Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

John S

Thanks MIke,
I will use your suggestions in my tutorial (I'm not a guru like you or Paul, so I would appreciate your input).   My tutorial is going to introduce concepts to the OOP challenged (which I am one).   

I was able create another example utilizing multiple inheritance ( Lab from Retriever from Dog ).
John Siino, Advanced Engineering Services and Software