This presentation was stolen from Chuck Anderson.
(:source lang=c++ :)
using namespace std;
int *copyInts(int *src, int num) { int *dest = new int[num]; for (int i=0; i<num; i++)
dest[i] = src[i];
return dest; }
double *copyDoubles(double *src, int num) { double *dest = new double[num]; for (int i=0; i<num; i++)
dest[i] = src[i];
return dest; }
char *copyLetters(char *src, int num) { char *dest = new char[num]; for (int i=0; i<num; i++)
dest[i] = src[i];
return dest; }
void printInts(int *src, int num) { for (int i=0; i<num; i++)
cout << src[i] << ' ';
cout << endl; }
void printDoubles(double *src, int num) { for (int i=0; i<num; i++)
cout << src[i] << ' ';
cout << endl; }
void printLetters(char *src, int num) { for (int i=0; i<num; i++)
cout << src[i] << ' ';
cout << endl; }
int main() { int ints[5] = {1, 2, 3, 4, 5}; double doubles[4] = {0.1, 0.2, 0.3, 0.4}; char letters[6] = {'a', 'b', 'c', 'd', 'e', 'f'};
int *ints2 = copyInts(ints,5); double *doubles2 = copyDoubles(doubles,4); char *letters2 = copyLetters(letters,6);
printInts(ints,5); printInts(ints2,5);
printDoubles(doubles,4); printDoubles(doubles2,4);
printLetters(letters,6); printLetters(letters2,6); } (:sourcend:)
When run, we see:
1 2 3 4 5 1 2 3 4 5 0.1 0.2 0.3 0.4 0.1 0.2 0.3 0.4 a b c d e f a b c d e f
Can this code be simplified? Yep. We don’t need printInts
,
printDoubles
and printLetters
. All can be named print
,
because the types of the compiler can disambiguate the calls to
print
by the types of the arguments.
(:source lang=c++ :)
using namespace std;
int *copyInts(int *src, int num) { int *dest = new int[num]; for (int i=0; i<num; i++)
dest[i] = src[i];
return dest; }
double *copyDoubles(double *src, int num) { double *dest = new double[num]; for (int i=0; i<num; i++)
dest[i] = src[i];
return dest; }
char *copyLetters(char *src, int num) { char *dest = new char[num]; for (int i=0; i<num; i++)
dest[i] = src[i];
return dest; }
void print(int *src, int num) { for (int i=0; i<num; i++)
cout << src[i] << ' ';
cout << endl; }
void print(double *src, int num) { for (int i=0; i<num; i++)
cout << src[i] << ' ';
cout << endl; }
void print(char *src, int num) { for (int i=0; i<num; i++)
cout << src[i] << ' ';
cout << endl; }
int main() { int ints[5] = {1, 2, 3, 4, 5}; double doubles[4] = {0.1, 0.2, 0.3, 0.4}; char letters[6] = {'a', 'b', 'c', 'd', 'e', 'f'};
int *ints2 = copyInts(ints,5); double *doubles2 = copyDoubles(doubles,4); char *letters2 = copyLetters(letters,6);
print(ints,5); print(ints2,5);
print(doubles,4); print(doubles2,4);
print(letters,6); print(letters2,6); } (:sourcend:)
And we still get:
1 2 3 4 5 1 2 3 4 5 0.1 0.2 0.3 0.4 0.1 0.2 0.3 0.4 a b c d e f a b c d e f
Can we do the same for the copy functions?
(:source lang=c++ :)
using namespace std;
int *copy(int *src, int num) { int *dest = new int[num]; for (int i=0; i<num; i++)
dest[i] = src[i];
return dest; }
double *copy(double *src, int num) { double *dest = new double[num]; for (int i=0; i<num; i++)
dest[i] = src[i];
return dest; }
char *copy(char *src, int num) { char *dest = new char[num]; for (int i=0; i<num; i++)
dest[i] = src[i];
return dest; }
void print(int *src, int num) { for (int i=0; i<num; i++)
cout << src[i] << ' ';
cout << endl; }
void print(double *src, int num) { for (int i=0; i<num; i++)
cout << src[i] << ' ';
cout << endl; }
void print(char *src, int num) { for (int i=0; i<num; i++)
cout << src[i] << ' ';
cout << endl; }
int main() { int ints[5] = {1, 2, 3, 4, 5}; double doubles[4] = {0.1, 0.2, 0.3, 0.4}; char letters[6] = {'a', 'b', 'c', 'd', 'e', 'f'};
int *ints2 = copy(ints,5); double *doubles2 = copy(doubles,4); char *letters2 = copy(letters,6);
print(ints,5); print(ints2,5);
print(doubles,4); print(doubles2,4);
print(letters,6); print(letters2,6); } (:sourcend:)
Yep. It works:
1 2 3 4 5 1 2 3 4 5 0.1 0.2 0.3 0.4 0.1 0.2 0.3 0.4 a b c d e f a b c d e f
Do you see further simplifications? No, unless there is some way to parameterize the type of the arguments when you define the functions.
(:source lang=c++ :)
using namespace std;
template <typename T> T *copy(T *src, int num) { T *dest = new T[num]; for (int i=0; i<num; i++)
dest[i] = src[i];
return dest; }
template <typename T> void print(T *src, int num) { for (int i=0; i<num; i++)
cout << src[i] << ' ';
cout << endl; }
int main() { int ints[5] = {1, 2, 3, 4, 5}; double doubles[4] = {0.1, 0.2, 0.3, 0.4}; char letters[6] = {'a', 'b', 'c', 'd', 'e', 'f'};
int *ints2 = copy(ints,5); double *doubles2 = copy(doubles,4); char *letters2 = copy(letters,6);
print(ints,5); print(ints2,5);
print(doubles,4); print(doubles2,4);
print(letters,6); print(letters2,6); } (:sourcend:)
Look at all the places where T was used and was not used in copy()
.
Templates can also be used to avoid redefining classes for different types. Here is an example. Let’s say we want to keep track of either integer or floating-point 2-D points:
(:source lang=c++ :)
using namespace std;
class PointInt { private: int x, y; public: PointInt(const int xarg, const int yarg) : x(xarg), y(yarg) { } void setx(const int xarg) {
x = xarg;
} void sety(const int yarg) {
y = yarg;
} int getx() const {
return x;
} int gety() const {
return y;
} };
class PointDouble { private: double x, y; public: PointDouble(const double xarg, const double yarg) : x(xarg), y(yarg) { } void setx(const double xarg) {
x = xarg;
} void sety(const double yarg) {
y = yarg;
} double getx() const {
return x;
} double gety() const {
return y;
} };
int main() { PointInt pi(1,2); PointDouble pd(3.4,2.5);
cout << '(' << pi.getx() << ',' << pi.gety() << ')' << endl; cout << '(' << pd.getx() << ',' << pd.gety() << ')' << endl;
} (:sourcend:)
When run, we see this output.
(1,2) (3.4,2.5)
Can we get rid of the duplications between PointInt
and
PointDouble
? Only if there is a way to parameterize the type of the
class.
(:source lang=c++ :)
using namespace std;
template <typename T> class Point { private: T x, y; public: Point(const T xarg, const T yarg) : x(xarg), y(yarg) { } void setx(const T xarg) {
x = xarg;
} void sety(const T yarg) {
y = yarg;
} T getx() const {
return x;
} T gety() const {
return y;
} };
int main() { // This next statement doesn’t work. For templates classes, must specify the type. // Point pi(1,2);
Point<int> pi(1,2); Point<double> pd(3.4,2.5);
cout << '(' << pi.getx() << ',' << pi.gety() << ')' << endl; cout << '(' << pd.getx() << ',' << pd.gety() << ')' << endl; } (:sourcend:)
You can have more than one parameter—just be sure you use the right type in the right place.
(:source lang=c++ :)
using namespace std;
template <typename Tx, typename Ty> class Point { private: Tx x; Ty y; public: Point(const Tx xarg, const Ty yarg) : x(xarg), y(yarg) { } void setx(const Tx xarg) {
x = xarg;
} void sety(const Ty yarg) {
y = yarg;
} Tx getx() const {
return x;
} Ty gety() const {
return y;
} };
int main() { // This next statement doesn’t work. For templates classes, must specify the type. // Point pi(1,2);
Point<int,int> pi(1,2); Point<double,double> pd(3.4,2.5);
Point<int,double> pid(5, 6.42);
cout << '(' << pi.getx() << ',' << pi.gety() << ')' << endl; cout << '(' << pd.getx() << ',' << pd.gety() << ')' << endl; cout << '(' << pid.getx() << ',' << pid.gety() << ')' << endl; } (:sourcend:)
Output is
(1,2) (3.4,2.5) (5,6.42)
You can also include a constant integer to specify things like the size of an array, and can have a default value.
(:source lang=c++ :)
using namespace std;
template <typename T, int N = 10> class Bunch { private: T data[N]; public: Bunch() {}
void setData(int index, const T &item) {
data[index] = item;
}
friend ostream &operator<< (ostream &out, Bunch<T,N> &b) {
for (int i=0; i<N; i++) out << b.data[i] << ' '; return out;
} };
int main() { Bunch<char,5> b;
b.setData(0,'a'); b.setData(1,'b'); b.setData(2,'c'); b.setData(3,'d'); b.setData(4,'e');
cout << b << endl; } (:sourceend:)
Produces the output:
a b c d e
Adding these lines at the end of main()
(:source lang=c++ :) Bunch<int> nums;
nums.setData(0,11); nums.setData(1,22);
cout << nums << endl; (:sourceend:)
produces:
a b c d e 11 22 4196139 0 9376000 0 4197223 0 9339136 0
Why the funky numbers?
Now, back to the points. We have parameterized the types of x and y in
Point
. Now, how do we make a collection, like an array, of
Point
objects, when Point
itself is parameterized?
(:source lang=c++ :)
using namespace std;
template <typename T> class Point { private: T x, y; public: Point() : x(0), y(0) { } Point(const T xarg, const T yarg) : x(xarg), y(yarg) { } void setx(const T xarg) {
x = xarg;
} void sety(const T yarg) {
y = yarg;
} T getx() const {
return x;
} T gety() const {
return y;
} };
template <typename T, template <typename> class P> // Whoa ... class Points { private: P<T> *data; int number; public: Points(const int num) : number(num), data(new P<T>[num]) {}
void set(int index, const T &x, const T &y) {
data[index].setx(x); data[index].sety(y);
}
friend ostream &operator<<(ostream &out, Points<T,P> &p) {
for (int i=0; i<p.number; i++) out << " (" << p.data[i].getx() << ',' << p.data[i].gety() << ')'; return out;
} };
int main() { Points<int,Point> points(3);
points.set(0,1,2); points.set(1,10,20); points.set(2,30,40);
cout << points << endl;
Points<double,Point> pointsd(3);
pointsd.set(0,1.1,2.2); pointsd.set(1,10.1,20.2); pointsd.set(2,30.1,40.2);
cout << pointsd << endl; } (:sourceend:)
Output is:
(1,2) (10,20) (30,40) (1.1,2.2) (10.1,20.2) (30.1,40.2)
Tough slogging ... but cool!
Modified: 2009-04-04T17:06 User: Guest Check: HTML CSSEdit History Source |
Apply to CSU |
Contact CSU |
Disclaimer |
Equal Opportunity Colorado State University, Fort Collins, CO 80523 USA © 2015 Colorado State University |