Pointers to Functions
|
Introduction
|
Imagine you are writing a program to process
cylinder-related calculations, that is, to get its diameter, its
circumference, its areas, and volume. You could start your program as
follows:
using namespace System; int main() { double Radius; Radius = 25.55; Console::WriteLine("Cylinder Summary"); Console::WriteLine("\nRadius: {0}", Radius); return 0; }
This would produce:
Cylinder Summary Radius: 25.55 Press any key to continue...
When we studied functions that return a value, we saw that
the result of such a function could be assigned to a value locally declared in the
calling function:
using namespace System;
static double CalculateDiameter(double R);
int main()
{
double Radius, Diameter;
Radius = 25.52;
Diameter = CalculateDiameter(Radius);
Console::WriteLine("Cylinder Summary";
Console::WriteLine("\nRadius: {0}", Radius);
Console::WriteLine("\nDiameter: {0}", Diameter);
return 0;
}
double CalculateDiameter(double Rad)
{
return Rad * 2;
}
At this time, we know that when a function returns a value,
the calling of the function is a complete value that can be assigned to a
variable. In fact, when calling a function that takes an argument, if that
argument itself is gotten from a value returned by a function, the calling of
the second function can be done directly when calling the first function. This
seemingly complicated scenario can be easily demonstrated as follows:
using namespace System;
static double CalculateDiameter(double R);
static double CalculateCircumference(double D);
int main()
{
double Radius, Circumference;
Radius = 25.52;
// Instead of calling the CalculateDiameter() function first and
// assign it to another, locally declared variable, such as in
// "double Diameter = CalculateDiameter(Radius)", we can call the
// CalculateDiameter(Radius) directly when we are calling the
// CalculateCircumference() function.
// This is possible because the CalculateCircumference() function
// takes an argument that is the result of calling the
// CalculateDiameter() function. As long as we only need the
// circumference and we don't need the diameter, we don't have
// to explicitly call the CalculateDiameter() function.
Circumference = CalculateCircumference(CalculateDiameter(Radius));
Console::WriteLine("Cylinder Summary";
Console::WriteLine("\nRadius: {0}", Radius);
Console::WriteLine("\nCircumference: {0}\n", Circumference);
return 0;
}
double CalculateDiameter(double Rad)
{
return Rad * 2;
}
double CalculateCircumference(double Diam)
{
const double PI = 3.14159;
return Diam * PI;
}
In some circumstances, such as this one, we may find out
that the value we want to process in a function is in fact a value gotten from
an intermediary function. Unfortunately, a regular function cannot be passed to
a function like a regular variable. In reality, the C++ language allows this but
the function must be passed as a pointer.
The concept of a callback function is highly used in
Microsoft Windows application programming. For this reason, you should
know how callback functions work and how to use them. They are present
on the Win32 API library. In Win32,
they allow creating procedures.
A callback function is a pointer to a function. A
pointer to a function is a special function that is declared as a
pointer. Its name by itself is considered a variable. As such, and
unlike a regular variable, the name of this function can be assigned a
regular function. This allows the function to be passed as an argument.
The function itself is not implemented but its name is used as a
programmer type-defined object.
The reason a function can be passed as argument is because
the name of a function is itself a constant pointer. The basic syntax to declare
a pointer to a function is:
DataType (*FunctionName)();
The DataType can be any of the data types we have
used so far. The FunctionName must be a valid name for a function.
The name of the function must be preceded by an asterisk operator. To actually
make this declaration a pointer to a function, the asterisk and the name of the
pointer must be included between parentheses. If you omit the parentheses, the
compiler would think that you are declaring a function that would return a
pointer, which changes everything.
Because this is a pointer, you must use parentheses,
required for every function declared. If this function will not take any
argument, you can leave the parentheses empty or type void. Based on this, you can declare a pointer to a function as
follows:
int main()
{
void (*SomethingToSay)(void);
return 0;
}
After declaring a pointer to a function, keep in mind that
this declaration only creates a pointer, not an actual function. In order to use
it, you must define the actual function that would carry the assignment the
function is supposed to perform. That function must have the same return type
and the same (number of) argument(s), if any. For example, the above declared
pointer to function is of type void and it does not take any
argument. you can define a function as follows:
void MovieQuote() { Console::WriteLine("We went through a lot of trouble because of you!"); Console::WriteLine("You owe us\n"); Console::WriteLine(" From \"Disorganized Crime\""); }
With such an associated function defined, you can assign it
to the name of the pointer to function as follows
SomethingToSay = MovieQuote;
This assignment gives life to the function declared as
pointer. The function can then be called as if it had actually been defined.
Here is an example:
using namespace System; void MovieQuote() { Console::WriteLine("We went through a lot of trouble because of you"); Console::WriteLine("You owe us"); Console::WriteLine(" From \"Disorganized Crime\""); } int main() { void (*SomethingToSay)(); // Assign the MovieQuote() function to the pointer to function SomethingToSay = MovieQuote; // Call the pointer to function as if it had been defined already SomethingToSay(); return 0; }
This would produce:
We went through a lot of trouble because of you You owe us From "Disorganized Crime" Press any key to continue...
A Function Pointer that Returns a Value
|
You can also declare a pointer to function for a function
that returns a value. Remember that both functions must return the same type of
value. Here is an example:
using namespace System; int Addition() { int a = 16, b = 442; return a + b; } int main() { int (*SomeNumber)(); // Assign the MovieQuote() function to the pointer to function SomeNumber = Addition; // Call the pointer to function as if it had been defined already Console::WriteLine("The number is {0}", SomeNumber()); return 0; }
A Function Pointer With Arguments
|
If you want to use a function that takes arguments, when
declaring the pointer to function, provide the return type and an optional name
for each argument. Here is an example:
int (*SomeNumber)(int x, int y);
When defining the associated function, besides returning the
same type of value, make sure that the function takes the same number of
arguments. Here is an example:
using namespace System; int Addition(int a, int b) { return a + b; } int main() { int (*SomeNumber)(int x, int y); int x = 128, y = 5055; // Assign the MovieQuote() function to the pointer to function SomeNumber = Addition; // Call the pointer to function as if it had been defined already Console::WriteLine("{0} + {1} = {2}\n", x, y, SomeNumber(x, y)); return 0; }
Type-Defining a Function Pointer
|
You can create a programmer-defined type as a pointer
to function. Here is the syntax to use:
typedef (*TypeName)(Arguments);
The typedef keyword must be used.
The asterisk and the TypeName must be enclosed in
parentheses. The name must follow the rules applied to functions so far.
The TypeName must be followed by parentheses. If the
pointer to function will take arguments, provide its type or their types between
parentheses. Otherwise, you can leave the parentheses empty (but you must
provide the parentheses).
After creating such a custom type, the name of the type
would be used as an alias to a pointer to function. Consequently, it can be used
to declare a pointer to function. Here is an example:
using namespace System; int Addition(int a, int b) { return a + b; } int main() { // Creating a programmer-defined type typedef int (*AddsTwoIntegers)(int x, int y); // Now, the AddsTwoIntegers name is a pointer to function // that can take two integers. It can be used for a declaration AddsTwoIntegers TwoNumbers; int x = 128, y = 5055; TwoNumbers = Addition; // Call the pointer to function as if it had been defined already Console::WriteLine("{0} + {1} = {2}\n", x, y, TwoNumbers(x, y)); return 0; }
This would produce:
128 + 5055 = 5183 Press any key to continue...
A Pointer to a Function as Argument
|
Using pointer to functions, a function can be passed as
argument to another function. The function must be passed as a pointer. The
argument is declared in a complete format as if you were declaring a function.
Here is an example of a function that is passed a function as argument.
double Circumference(double (*FDiam)(double R))
This Circumference() function takes one argument. The
argument itself is a pointer to function. This argument takes a
double-precision number as argument and it returns a double-precision value. The
Circumference() function returns a double-precision number.
It is important to know that the pointer to function that is
passed as argument is declared completely, in this case as
double (*FDiam)(double R)
Although the FDiam declaration is accompanied by an
argument, in this case R, this argument allows the compiler to know that FDiam
takes an argument. This argument actually will not be processed by the
Circumference() function when the Circumference() function is defined because
the R argument does not belong to the Circumference() function.
When calling the Circumference() function, you will use the
FDiam argument as a variable in its own right, using its name, as in
Circumference(Diameter)
When defining the Circumference() function, you must process
the pointer to function that it takes as argument. If this argument is an alias
to a function that returns a value, you can call it and pass it the argument as
we studied in the last section. If you want to involve the FDiam argument in any
operation, you can declare a local variable to the Circumference() function. If
the FDiam argument must be involved in an operation that involves a value
external to the Circumference() function, you must pass that type of value as
argument to the Circumference() function, unless you are using a global variable. This means
that, in most circumstances, the pointer to function passed as argument may be
accompanied by at least one other argument. For example, if you want to use the
FDiam as a diameter value to calculate the circumference (Circumference =
Diameter * PI), you may have to declare it with an argument for the radius. It
would be declared as follows:
double Circumference(double (*FDiam)(double R), double Rad);
The function can then be implemented as follows:
double Circumference(double (*FDiam)(double R), double Rad) { double Circf; const double PI = 3.14159; Circf = (*FDiam)(Rad); return Circf * PI; }
Remember that, when declaring a function, the compiler does
not care about the name(s) of the argument(s). If the function takes any, what
the compiler cares about are the return type of the function, its name, and the
type(s) of its argument(s), if any. Therefore, the above function could as well
be declared as follows:
double Circumference(double (*)(double R), double);
This indicates that the Circumference() function takes two
arguments whose names are not known. The first argument is a pointer to a
function that takes one double-precision number as argument and returns a
double. The second argument of the Circumference() function is also a
double-precision number. The Circumference() function returns a double-precision
number. This is what the program at this time would look like:
using namespace System; double Diameter(double); double Circumference(double (*D)(double R), double r); int main() { double Radius; Radius = 25.52; Console::WriteLine("Cylinder Summary"); Console::WriteLine("Radius: {0}", Radius); Console::WriteLine("Circumference = {0}\n", Circumference(Diameter, Radius)); return 0; } double Diameter(double Rad) { return Rad * 2; } double Circumference(double (*FDiam)(double R), double Rad) { double Circf; const double PI = 3.14159; Circf = (*FDiam)(Rad); return Circf * PI; }
This would produce:
Cylinder Summary Radius: 25.52 Circumference = 160.347 Press any key to continue...
To simplify the declaration of a pointer to function, we saw
that you can create a programmer-defined type using the typedef keyword.
This can also help when passing a function as argument. Here is an example:
typedef double (*FDiam)(double R); double Circumference(FDiam, double);
When creating such a programmer-defined type, remember that
you must give a name to the alias, in this case FDiam. After this creation,
FDiam is an alias to a pointer to function of a double-precision type and which
takes one double-precision number as argument.
Remember, as we learned when studying functions that return
a value, that the item on the right side of the return keyword can be a value or
a complete expression. Therefore, you can simplify the implementation of the
Circumference() function as follows:
double Circumference(double (*FDiam)(double R), double Rad)
{
const double PI = 3.14159;
return (*FDiam)(Rad) * PI;
}
Classes and Pointers to Functions
|
A Pointer to Function as a Member Variable
|
When studying pointers to functions,
we learned to use a programmer's type-defined declaration to create an alias to
a pointer to function. We used an example such as the following:
typedef double (*Multiply)(const double a);
With this declaration, the word Multiply can be used in
place of a function that takes a double-precision value as argument and the
function returns a double. We learned that such a Multiply name could be
used to declare a variable that in fact would be used as a function. Based on
this feature of the C++ language, the Multiply name can also be used to declare
a member variable of a class. Such a member variable would be interpreted as a
function. The declaration can be done as follows:
typedef double (*Multiply)(const double a); public value class CSquare { public: Multiply Perimeter; };
As done with the regular pointer to function, you don't
implement the Perimeter member. In fact, so far, the compiler doesn't know what
to do with the word Perimeter. Therefore, you must formally define a function
that can implement the behavior that the Perimeter member is supposed to use.
Such a function can be defined as follows:
double Perimetre(const double x) { return x * 4; }
Even with this definition of the Perimetre() function, there
is no relationship between the Perimeter member of the CSquare class and the
Perimetre() independent function, and the compiler doesn't see any relationship
between them. This means that, until you join these two functions, the compiler
would not know what to do with the member of the class. To use the Perimeter
member of the CSquare class, you should first assign it an appropriate function
that has the same signature as its alias. Then you can use the member of the
class as if it were a regular method of the class. Here is an example of how
this would be done:
using namespace System; typedef double (*Multiply)(const double a); public value class CSquare { public: Multiply Perimeter; }; double Perimetre(const double x) { return x * 4; } int main() { CSquare Sqr; double Side = 25.55; Sqr.Perimeter = Perimetre; Console::WriteLine("Square Characteristics"); Console::WriteLine("Side: {0}", Side); Console::WriteLine("Perimeter: {0}", Sqr.Perimeter(Side)); return 0; }
Using this same approach, you can declare different types of
pointers to function as you see fit and using the function signature(s) of your
choice. Here is an example:
using namespace System; typedef double (*Multiply)(const double a); typedef double (*Multiple)(const double x, const double y); public value class CSquare { public: Multiply Perimeter; Multiple Area; }; double Perimetre(const double x) { return x * 4; } double Surface(const double x, const double y) { return x * y; } int main() { CSquare Sqr; double Side = 25.55; Sqr.Perimeter = Perimetre; Sqr.Area = Surface; Console::WriteLine("Square Characteristics"); Console::WriteLine("Side: {0}", Side); Console::WriteLine("Perimeter: {0}", Sqr.Perimeter(Side)); Console::WriteLine("Area: {0}", Sqr.Area(Side, Side)); return 0; }
This would produce:
Square Characteristics Side: 25.55 Perimeter: 102.2 Area: 652.8025 Press any key to continue . . .
In the class, we use the Multiply word as a type, the same
way we would use a normal data type, to declare a variable. Although this looks
like that, it follows different rules. While you can declare two variables using
the same data type, you cannot declare two pointer to methods using the same
type:
public value class CSquare
{
public:
// This works
double a, b;
// This produces an error
Multiply Perimeter, Area;
};
If you do this, you would receive an error when you compile
the application.
Delegates
|
Introduction
|
As a variable can be declared as a pointer so can a
function. As we saw in the previous sections, a pointer to a function is a type of variable whose name can be used
as a variable although it is not a traditional variable like the others. This concept
has always been very helpful in Microsoft Windows programming because it allows
the use of callback functions. Thanks to their effectiveness (and legacy code),
the concept of callback functions was carried out in the .NET Framework but they were defined with the name of
delegate.
A delegate is a special type of user-defined variable that references a method of
a class. There are similarities and differences between a function pointer and a
delegate:
- Like a function pointer, a delegate is not defined like a normal function
- Like a function pointer, a delegate has the appearance of a variable that is declared as a pointer
- Like a function pointer, a delegate can take argument(s)
- While a function pointer can be associated with a global function, that is, a function that is not a member of a class, a delegate can be associated with, or can reference, only a member of a class
- Start Microsoft Visual C++ 2005
- On the main menu, click File -> New -> Project...
- On the left side, make sure that Visual C++ is selected. In the Templates list, click CLR Empty Project
- In the Name box, replace the string with FlowerShop1 and click OK
- On the main menu, click Project -> Add Class...
- In the Categories lists, expand Visual C++ and click C++.
In the Templates list, make sure C++ Class is selected and click Add - Set the Name of the class to CFlower and click Finish
- Complete the Flower.h header file as follows:
#pragma once using namespace System; public ref class CFlower { private: String ^ _tp; String ^ _clr; String ^ _arg; double _price; public: property String ^ Type { String ^ get() { return _tp; } void set(String ^ tp) { _tp = tp; } } property String ^ Color { String ^ get() { return _clr; } void set(String ^ clr) { _clr = clr; } } property String ^ Arrangement { String ^ get() { return _arg; } void set(String ^ arg) { _arg = arg; } } property double UnitPrice { double get() { return _price; } void set(double price) { _price = price; } } public: CFlower(void); CFlower(String ^ type, String ^ color, String ^ argn, double price); ~CFlower(); void Show(); };
- Complete the Flower.cpp source file as follows:
#include "Flower.h" CFlower::CFlower(void) : _tp(L"Roses"), _clr(L"Red"), _arg(L"Basket"), _price(45.95) { } CFlower::CFlower(String ^ type, String ^ color, String ^ argn, double price) { _tp = type; _clr = color; _arg = argn; _price = price; } CFlower::~CFlower() { } void CFlower::Show() { Console::WriteLine(L"======================="); Console::WriteLine(L"==-=-=Flower Shop=-=-=="); Console::WriteLine(L"-----------------------"); Console::WriteLine(L"Flower Type: {0}", Type); Console::WriteLine(L"Flower Color: {0}", Color); Console::WriteLine(L"Arrangement: {0}", Arrangement); Console::WriteLine(L"Price: {0:C}", UnitPrice); Console::WriteLine(L"======================="); }
- To create one more source file, on the main menu, click Project -> Add New Item...
- In the Templates list, make sure C++ File (.cpp) is selected.
Set the Name to Exercise and click Add - Complete the file as follows:
#include "Flower.h" using namespace System; int main() { CFlower ^ flower = gcnew CFlower(L"Lilies", L"Pink", L"Bouquet", 45.65); flower->Show(); Console::WriteLine(); return 0; }
- Execute the application to make sure it can compile:
======================= ==-=-=Flower Shop=-=-== ----------------------- Flower Type: Lilies Flower Color: Pink Arrangement: Bouquet Price: $45.65 ======================= Press any key to continue . . .
- Close the DOS window
To declare a delegate, you use the delegate keyword.
The basic formula used to create a delegate is:
Access Level delegate Function-Signature;
The Function-Signature factor is created as you would
declare a normal C++ function. This means that the Function-Signature
must have
- A return type: The return type can be void, one of the primitive data types (int, long, double, short, etc), a .NET Framework type (Double, Decimal, etc), a .NET Framework class, or a class you created
- A name: Like every variable, function, or class, a delegate must have a name
- Parentheses: Like every function, a delegate must have parentheses
- Optional arguments: If the method(s) that this delegate will reference has/have argument(s), this delegate must also have argument(s) of exactly the same kind
After defining the Function-Signature, you must end
the delegate declaration with a semi-colon. Here is an example:
delegate double Addition();
A declaration of a delegate can also use an access level as public
or private. If you plan to use the delegate only locally, you can omit
the access level or declare it as private, which is the default. If you plan to
use the delegate outside of the project in which it is created, you should
declare it as public.
After declaring a delegate, remember that it only provides a
skeleton for a method, not an actual method. In order to use it, you must define
a method that would carry an assignment the method is supposed to perform. That
method must have the same return type and the same (number of) argument(s), if
any. Here is an example:
private delegate double Addition();
public ref class CMathOperations
{
public:
double Plus()
{
double a = 248.66, b = 50.28;
return a + b;
}
};
After implementing the method, you can associate it to the
name of the delegate. To do that, where you want to use the method, first
declare a pointer of the type of the delegate using the gcnew operator
and calling its default constructor.
|
- To create a delegate, declare the following in the Exercise.cpp file:
#include "Flower.h" using namespace System; private delegate void Display(); int main() { return 0; }
- Save the file
Techniques of Using a Delegate
|
After properly declaring and initializing the instance, it
becomes ready and you can use it. When you declare a delegate, at run time, the
compiler generates a class for it. The generated class automatically receives
the following characteristics:
- The class is derived from the System::MulticastDelegate class which itself derives from the System::Delegate class. Both classes are defined in the System.dll assembly
- The class is equipped with a default constructor and two other
constructors that takes two arguments as follows:
protected: MulticastDelegate(); protected: MulticastDelegate(Object^ target, String^ method); protected: MulticastDelegate(Type^ target, String^ method);
- The class is automatically equipped with a method called Invoke. Its return type and its argument become the same as those of the declared delegate
As mentioned in the second point, the instance of the
delegate can take two arguments. If you declared the method as done earlier (delegate
double Addition();), the first argument of the instance must be a pointer to the
class that implements the method associated with the delegate. This would be
done as follows:
using namespace System; private delegate double Addition(); public ref class CMathOperations { public: double Plus() { double a = 248.66, b = 50.28; return a + b; } }; int main() { CMathOperations ^ oper = gcnew CMathOperations; Addition ^ add = gcnew Addition(oper, . . .); return 0; }
After the first argument, the second argument must be a
reference to the class' method that implements the desired behavior of the
delegate. Here is an example:
using namespace System;
private delegate double Addition();
public ref class CMathOperations
{
public:
double Plus()
{
double a = 248.66, b = 50.28;
return a + b;
}
};
int main()
{
CMathOperations ^ oper = gcnew CMathOperations;
Addition ^ add = gcnew Addition(oper, &CMathOperations::Plus);
return 0;
}
The declaration gives meaning to the delegate. To
actually use the method, you can call the name of the delegate as if it were a defined
method. If you want to retrieve the value that the delegate produces, in the
above third point, we mentioned that the delegate's class is equipped with a
method called Invoke that completely "understands" the method that
implements it. This means that the Invoke() method returns the same value as the
associated method. In our example, the Invoke() method of the delegate would
return a double since the delegate was declared as returning a double.
Here is an example that uses the delegate:
using namespace System;
private delegate double Addition();
public ref class CMathOperations
{
public:
double Plus()
{
double a = 248.66, b = 50.28;
return a + b;
}
};
int main()
{
CMathOperations ^ oper = gcnew CMathOperations;
Addition ^ add = gcnew Addition(oper, &CMathOperations::Plus);
Console::WriteLine(L"Value = {0}", add->Invoke());
return 0;
}
This would produce:
Value = 298.94 Press any key to continue . . .
We mentioned earlier that you could declare the associated
method of a delegate like any normal method, in which case you must pass a
pointer to the class as the first argument of the instance of the delegate. This
is a requirement if you plan to instantiate the delegate outside of the class
that defines its associated method. That's the case for our MathOperations
class. If
you plan to use a delegate in the same class that implements its associated
method, you can declare the method static. Here is an example:
private delegate double Addition(); private delegate double Multiplication(); public ref class CMathOperations { public: double Plus() { double a = 248.66, b = 50.28; return a + b; } static double Times() { double a = 248.66, b = 50.28; return a * b; } };
Once again, to use the delegate, you must first declare its
pointer and allocate its memory using the gcnew operator and calling the
constructor that takes two arguments. This time, the first argument must be null
or 0. The second argument must still be a reference to the method that
implements the desired behavior. Here is an example:
using namespace System; private delegate double Addition(); private delegate double Multiplication(); public ref class CMathOperations { public: CMathOperations() { multi = gcnew Multiplication(&Times); } double Plus() { double a = 248.66, b = 50.28; return a + b; } static double Times() { double a = 248.66, b = 50.28; return a * b; } void ShowResult() { Console::WriteLine(L"Multiplication = {0}", multi->Invoke()); } private: Multiplication ^ multi; }; int main() { CMathOperations ^ oper = gcnew CMathOperations; Addition ^ add = gcnew Addition(oper, &CMathOperations::Plus); Console::WriteLine(L"Addition = {0}", add->Invoke()); oper->ShowResult(); return 0; }
This would produce:
Addition = 298.94 Multiplication = 12502.6248 Press any key to continue . . .
Because delegates are usually declared globally,
that is, outside of a class, they can be associated with a method of any class,
provided the method has the same return type (and the same (number of)
argument(s)) as the delegate.
|
- To implement and use a delegate, change the Exercise.cpp source file as
follows:
#include "Flower.h" using namespace System; private delegate void Display(); int main() { CFlower ^ flower = gcnew CFlower; flower->Type = L"Daisies"; flower->Color = L"Yellow"; flower->Arrangement = L"Vase"; flower->UnitPrice = 45.65; Display ^ disp = gcnew Display(flower, &CFlower::Show); disp->Invoke(); Console::WriteLine(); return 0; }
- Execute the application to see the result:
======================= ==-=-=Flower Shop=-=-== ----------------------- Flower Type: Daisies Flower Color: Yellow Arrangement: Vase Price: $45.65 ======================= Press any key to continue . . .
- Close the DOS window
Delegates Compositions
|
One of the characteristics that set delegates apart from
C/C++ function pointers is that one delegate can be added to another using the
overloaded addition assignment operator. This is referred to as composition.
A Delegate With One of More Arguments
|
If you want to associate a method that takes arguments to a
delegate, when declaring the delegate, provide the necessary argument(s) in its
parentheses. Here is an example of a delegate that takes two arguments (and
returns a value):
delegate double Addition(double x, double y);
When defining the associated method, besides returning the
same type of value if not void, make sure that the method takes the same number
of arguments. Here is an example:
private delegate double Addition(double x, double y);
private delegate double Multiplication(double x, double y);
public ref class CMathOperations
{
public:
double Plus(double x, double y)
{
return x + y;
}
};
Once again, to associate the method, declare a variable of
the type of delegate and pass a reference to the method as the second argument to the constructor of the
delegate. Here is an example:
private delegate double Addition(double x, double y); private delegate double Multiplication(double x, double y); public ref class CMathOperations { public: CMathOperations() { add = gcnew Addition(&Plus); multi = gcnew Multiplication(&Times); } static double Plus(double x, double y) { return x + y; } static double Times(double x, double y) { return x * y; } private: Addition ^add; Multiplication ^ multi; };
Notice that only the name of the method is passed to the
delegate. To actually use the value that the delegate produces, when calling its
Invoke() method, you must pass the exact number of arguments with their
correct values:
using namespace System; private delegate double Addition(double x, double y); private delegate double Multiplication(double x, double y); public ref class CMathOperations { public: CMathOperations() { add = gcnew Addition(&Plus); multi = gcnew Multiplication(&Times); } static double Plus(double x, double y) { return x + y; } static double Times(double x, double y) { return x * y; } void ShowResult() { Console::WriteLine(L"Addition = {0}", add->Invoke(24.56, 98.76)); Console::WriteLine(L"Multiplication = {0}", multi->Invoke(24.56, 98.76)); } private: Addition ^add; Multiplication ^ multi; }; int main() { CMathOperations ^ oper = gcnew CMathOperations; oper->ShowResult(); return 0; }
This would produce:
Addition = 123.32 Multiplication = 2425.5456 Press any key to continue . . .
|
- To add an argument to a method, change the Flower.h header file as
follows:
#pragma once using namespace System; public ref class CFlower { private: String ^ _tp; String ^ _clr; String ^ _arg; double _price; public: . . . public: CFlower(void); CFlower(String ^ type, String ^ color, String ^ argn, double price); ~CFlower(); void Show(const int qty); };
- To implement the method, change the Flower.cpp source file as follows:
#include "Flower.h" . . . void CFlower::Show(const int qty) { double totalPrice = UnitPrice * qty; Console::WriteLine(L"======================="); Console::WriteLine(L"==-=-=Flower Shop=-=-=="); Console::WriteLine(L"-----------------------"); Console::WriteLine(L"Flower Type: {0}", Type); Console::WriteLine(L"Flower Color: {0}", Color); Console::WriteLine(L"Arrangement: {0}", Arrangement); Console::WriteLine(L"Price: {0:C}", UnitPrice); Console::WriteLine(L"Quantity: {0}", qty); Console::WriteLine(L"Total Price: {0:C}", totalPrice); Console::WriteLine(L"======================="); }
- To use a delegate with argument, change the Exercise.cpp source file as
follows:
#include "Flower.h" using namespace System; private delegate void Display(const int x); int main() { CFlower ^ flower = gcnew CFlower; flower->Type = L"Mixed"; flower->Color = L"Various"; flower->Arrangement = L"Bouquet"; flower->UnitPrice = 34.85; Display ^ disp = gcnew Display(flower, &CFlower::Show); disp->Invoke(3); Console::WriteLine(); return 0; }
- Execute the application to see the result:
======================= ==-=-=Flower Shop=-=-== ----------------------- Flower Type: Mixed Flower Color: Various Arrangement: Bouquet Price: $34.85 Quantity: 3 Total Price: $104.55 ======================= Press any key to continue . . .
- Close the DOS window
A Delegate Passed as Argument
|
Using delegates, one method can be indirectly passed as
argument to another method. To proceed, first declare the necessary delegate.
Here is a example of such a delegate:
public delegate double Squared(double x);
A delegate can be passed as argument to a method. Such an
argument would be used as if it were a method itself. This means that, when
accessed in the body of the method, the name of the delegate must be accompanied
by parentheses and if the delegate takes an argument or arguments, the argument(s)
must be provided in the parentheses of the called delegate. Here is an example:
public delegate double Squared(double x);
public ref class CCircle
{
private:
double _radius;
public:
double Area(Squared ^ sqd)
{
return sqd(_radius) * Math::PI;
}
public:
property double Radius
{
double get() { return _radius; }
void set(double r) { _radius = r; }
}
};
After declaring a delegate, remember to define a method that
implements the needed behavior of that delegate. Here is an example:
public delegate double Squared(double x);
public ref class CCircle
{
private:
double _radius;
public:
double Area(Squared ^ sqd)
{
return sqd(_radius) * Math::PI;
}
static double ValueTimesValue(double Value)
{
return Value * Value;
}
public:
property double Radius
{
double get() { return _radius; }
void set(double r) { _radius = r; }
}
};
You can also define the associated method in another class,
not necessarily in the class where the delegate would be needed. Once the method
that implements the delegate is known, you can use the delegate as you see fit.
To do that, you can declare a pointer of the type of that delegate and pass the
implementing method to its constructor. Here is an example:
public delegate double Squared(double x);
public ref class CCircle
{
private:
double _radius;
public:
double Area(Squared ^ sqd)
{
return sqd(_radius) * Math::PI;
}
static double ValueTimesValue(double Value)
{
return Value * Value;
}
public:
property double Radius
{
double get() { return _radius; }
void set(double r) { _radius = r; }
}
void CircleCharacteristics()
{
Squared ^ Sq = gcnew Squared(ValueTimesValue);
}
};
This declaration gives life to the delegate and can then be
used as we have proceed with delegates so far. Here is an example:
using namespace System; public delegate double Squared(double x); public ref class CCircle { private: double _radius; public: double Area(Squared ^ sqd) { return sqd(_radius) * Math::PI; } static double ValueTimesValue(double Value) { return Value * Value; } public: property double Radius { double get() { return _radius; } void set(double r) { _radius = r; } } }; int main() { CCircle ^ round = gcnew CCircle; Squared ^ sqr = gcnew Squared(CCircle::ValueTimesValue); round->Radius = 32.48; Console::WriteLine(L"Circle Characteristics"); Console::WriteLine(L"Radius: {0}", round->Radius); Console::WriteLine(L"Area: {0}", round->Area(sqr)); return 0; }
This would produce:
Circle Characteristics Radius: 32.48 Area: 3314.22442654161 Press any key to continue . . .
No comments:
Post a Comment