Файл: Interfacing with C plus plus-programing communication with microcontrolers (K. Bentley, 2006).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 14.06.2025
Просмотров: 3608
Скачиваний: 1
8 DRIVING MOTORS - DC & STEPPER 239
delete MotorPtr;
Since MotorPtr is a pointer to the abstract base class Motor, the previous statement will call the default destructor of the Motor class and will only deallocate the space occupied by a Motor class object. The delete statement will not free the memory space occupied by the actual object in use (such as DCMotor), thereby generating a memory leak. We can demonstrate this event using an example program that has a simple class structure and a simple main() function as shown in Listing 8-14. Note: to further simplify the example, the Base and Derived classes do not have data members. We have also included cout statements within the body of the two destructors to show when each destructor is called. If the destructors did not have these cout statements, they would be identical to the default destructors generated by the compiler.
Listing 8-14 Use of non-virtual destructors.
#include <conio.h> #include <iostream.h>
class Base
{
public:
Base(){}
~Base()
{
cout << "Base type object deleted" << endl;
}
};
class Derived : public Base
{
public:
Derived(){};
~Derived()
{
cout << "Derived type object deleted " << endl;
}
};
void main()
{
Base *BasePtr;
BasePtr = new Derived; // BasePtr points to an object // of type Derived.
240 8 DRIVING MOTORS - DC & STEPPER
delete BasePtr; |
// Deletes object |
}
The pointer identifier BasePtr is declared to be of type Base. However, it is used to point to a dynamically allocated class object that is of type Derived. We use the following statement with the intention of deleting the dynamically allocated object of class Derived:
delete BasePtr;
We would expect this statement to call the destructor of the derived class. However, the following message is displayed when this program is executed:
Base type object deleted
This indicates that the destructor of the Derived class has not been called as intended to destroy the dynamically allocated Derived type object. We change the program to operate correctly by making the destructor ~Base() virtual. The modified program listing is shown in Listing 8-15.
Listing 8-15 Use of virtual destructors.
#include <conio.h> #include <iostream.h>
class Base
{
public:
Base(){} virtual ~Base()
{
cout << "Base type object deleted" << endl;
}
};
class Derived : public Base
{
public:
Derived(){};
~Derived()
{
cout << "Derived type object deleted " << endl;
}
};
void main()
8 DRIVING MOTORS - DC & STEPPER 241
{
Base *BasePtr; |
||
BasePtr = new Derived; |
// BasePtr |
points to an object |
// of type Derived. |
||
delete BasePtr; // Deletes object |
||
}
Note that in Listing 8-15 the keyword virtual is added in front of the destructor name ~Base(). This provides the link to all virtual destructors down to the next level of the class hierarchy so the correct destructors will be called. If you run this program you will see the following printed on the screen:
Derived type object deleted
Base type object deleted
This demonstrates that the delete statement has called the Derived class destructor and the Base class destructor, properly relinquishing the memory allocated for the Derived and Base class objects. Note: when an object of a derived class is instantiated, the constructor function of the base class is called first followed by a call to the constructor of the derived class. The derived class inherits the members of the base class that are instantiated in this manner. Therefore, it is important to include virtual destructors so that any memory allocation from within the derived class and its base class is properly relinquished.
Now we can return our attention to the motor control program. To allow the delete statements to properly de-allocate the dynamically allocated objects, we must provide a set of destructors; one destructor for each class of the motor class hierarchy, and we must make them virtual destructors. The bodies of these destructors can be empty. We simply need to establish a network of virtual destructors throughout the class hierarchy so that proper late binding will take place for the destructors. The modified class definitions are given in Listing 8-16 through to Listing 8-18.
C++ |
Virtual Destructor Names |
In a class hierarchy all virtual functions must have the same function signature;
i.e. they must have the same function name, same number of formal arguments
and the same types of formal arguments in each virtual function. However,
virtual destructors have different names throughout the hierarchy. Despite
having different destructor function signatures, late binding will enable the
correct set of destructors to be deployed in response to a delete statement.
242 8 DRIVING MOTORS - DC & STEPPER
Listing 8-16 AbstractMotor class with virtual destructor.
class AbstractMotor
{
private:
int Speed;
public:
AbstractMotor();
void SetSpeed(int speed); int GetSpeed();
virtual void Off()=0; virtual void Forward()=0; virtual void Reverse()=0; virtual void Brake()=0; virtual ~AbstractMotor(){}
};
Listing 8-17 ParallelPort class with virtual destructor.
class ParallelPort
{
private:
unsigned int BaseAddress; unsigned char InDataPort1;
public:
ParallelPort(); ParallelPort(int baseaddress);
void WritePort0(unsigned char data); void WritePort2(unsigned char data); unsigned char ReadPort1();
virtual ~ParallelPort(){}
};
Listing 8-18 Motor class with virtual destructor.
class Motor : public AbstractMotor, public ParallelPort
{
public:
Motor(int baseaddress=0x378); void Off();
virtual void Forward()=0;
8 DRIVING MOTORS - DC & STEPPER 243
virtual void Reverse()=0; virtual void Brake()=0; virtual ~Motor(){}
};
Now that we have proper destructors in our classes, we can write a complete program using virtual functions to control a motor and free memory as intended. Such a program is shown in Listing 8-19. Note that virtual destructors are not added to the DCMotor class or the StepperMotor class since these two classes are the terminal classes of the hierarchy. However, the program would still function properly if virtual destructors had been added to these two classes.
NOTE
Ensure the interface board is unpowered before connecting any type of motor. This needs to be done for the following reason.
Before first running the program, the port controlling the motor will not be under
control of the program and may be in an unknown state. This unknown state can
be such that the port’s logic states would drive the transistors to short-circuit the
motor’s power supply (damaging the transistors and possibly the power supply).
The program instructs the user to apply power to the board once it has set these
bits to a safe state. When the program ends, it sets the used bits of the port to a safe state to prevent any damage to the transistors or the power supply.
Connect a DC motor to the interface board as given in Table 8-7 and Table 8-9.
Stepper motors are connected to the interface board as shown in Figure 8-25 and
Figure 8-26.
If the motor does not drive as expected, first check for incorrect connections.
Listing 8-19 Complete program to control 'a Motor' using Virtual Functions.
//**************************************************
//Program to operate a Motor using Virtual Functions.
//**************************************************
#include <dos.h> #include <conio.h> #include <stdio.h> #include <stdlib.h> #include <iostream.h>
class ParallelPort
{
private:
244 8 DRIVING MOTORS - DC & STEPPER
unsigned int BaseAddress; unsigned char InDataPort1;
public:
ParallelPort(); ParallelPort(int baseaddress);
void WritePort0(unsigned char data); void WritePort2(unsigned char data); unsigned char ReadPort1();
virtual ~ParallelPort(){}
};
ParallelPort::ParallelPort()
{
BaseAddress = 0x378; InDataPort1 = 0;
}
ParallelPort::ParallelPort(int baseaddress)
{
BaseAddress = baseaddress; InDataPort1 = 0;
}
void ParallelPort::WritePort0(unsigned char data)
{
outportb(BaseAddress,data);
}
void ParallelPort::WritePort2(unsigned char data)
{
outportb(BaseAddress+2,data ^ 0x0B);
}
unsigned char ParallelPort::ReadPort1()
{
InDataPort1 = inportb(BaseAddress+1);
//Inverting Most significant bit to compensate
//for internal inversion by printer port hardware. InDataPort1 ^= 0x80;
//Filter to clear unused data bits D0, D1 and D2 to zero. InDataPort1 &= 0xF8;
return InDataPort1;
}
8 DRIVING MOTORS - DC & STEPPER 245
class AbstractMotor
{
private:
int Speed;
public:
AbstractMotor();
void SetSpeed(int speed); int GetSpeed();
virtual void Off()=0; virtual void Forward()=0; virtual void Reverse()=0; virtual void Brake()=0; virtual ~AbstractMotor(){}
};
AbstractMotor::AbstractMotor()
{
Speed =0;
}
void AbstractMotor::SetSpeed(int speed)
{
Speed = speed;
if(Speed > 255) Speed = 255; // Limit upper value if(Speed < 0) Speed = 0; // Limit lower value
}
int AbstractMotor::GetSpeed()
{
return Speed;
}
class Motor : public AbstractMotor, public ParallelPort
{
public:
Motor(int baseaddress=0x378); void Off();
virtual void Forward()=0; virtual void Reverse()=0; virtual void Brake()=0; virtual ~Motor(){}
};
Motor::Motor(int baseaddress): ParallelPort(baseaddress)
246 8 DRIVING MOTORS - DC & STEPPER
{
Off();
}
void Motor::Off()
{
WritePort0(0x00);
}
class DCMotor : public Motor
{
public:
DCMotor(int baseaddress=0x378); virtual void Forward();
virtual void Reverse(); virtual void Brake();
};
DCMotor::DCMotor(int baseaddress):Motor(baseaddress)
{
}
void DCMotor::Forward()
{
int j;
for(j = 0; j < GetSpeed(); j++) WritePort0(0x09);
for(;j < 256; j++) WritePort0(0x00);
}
void DCMotor::Reverse()
{
int j;
for(j = 0; j < GetSpeed(); j++) WritePort0(0x06);
for(;j < 256; j++) WritePort0(0x00);
}
void DCMotor::Brake()
{
WritePort0(0x0C);
}
8 DRIVING MOTORS - DC & STEPPER 247
enum MOTORTYPE {UPFS, UPHS, BPFS, BPHS};
class StepperMotor : public Motor
{
private:
MOTORTYPE MotorType; unsigned char Switching[8]; int CycleIndex;
int MaxIndex;
public:
StepperMotor(MOTORTYPE motortype = UPFS, int baseaddress=0x378);
virtual void Forward(); virtual void Reverse(); virtual void Brake();
};
StepperMotor::StepperMotor(MOTORTYPE motortype,
int baseaddress): Motor(baseaddress)
{
MotorType = motortype; CycleIndex = 0;
switch(MotorType)
{
case UPFS: MaxIndex = 4; Switching[0] = 0x11; Switching[1] = 0x12; Switching[2] = 0x22; Switching[3] = 0x21; break;
case UPHS: MaxIndex = 8; Switching[0] = 0x01; Switching[1] = 0x11; Switching[2] = 0x10; Switching[3] = 0x12; Switching[4] = 0x02; Switching[5] = 0x22; Switching[6] = 0x20; Switching[7] = 0x21; break;
case BPFS: MaxIndex = 4; Switching[0] = 0x99; Switching[1] = 0x69;