20
EE3490: Kỹ thuật lập trình HK1 2018/2019 TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội Chapter 11: Lập trình khái quát (Generic programming) 1

Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

  • Upload
    others

  • View
    16

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Chapter 11: Lập trình khái quát

(Generic programming)

1

Page 2: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Khuôn mẫu hàm

(Function templates)

2

Page 3: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

General

Đôi khi ta muốn viết một lần nhưng có thể tạo ra các hàm

với tham số thuộc nhiều kiểu khác nhau, thay vì phải viết

chồng nhiều hàm tương tự nhau int max(int a, int b) { return a>b ? a:b; }

double max(double a, double b) { return a>b ? a:b; }

float max(float a, float b) { return a>b ? a:b; }

lập trình ở mức độ khái quát cao hơn: coi kiểu của biến cũng là

tham số (type parameterization)

Khuôn mẫu hàm (function template): là khái niệm giúp

định nghĩa những hàm mà chưa xác định kiểu của các

tham số

Có thể hiểu là viết gộp chung các hàm chồng giống nhau về mặt

thuật toán

Kiểu của các tham số là tham số của khuôn mẫu

3

Page 4: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Định nghĩa hàm khái quát

Ví dụ 1: template <typename T>

T max(T a, T b)

{ return (a > b) ? a : b; }

T được giả định là kiểu của các tham số a, b và biến c

T sẽ được xác định khi gọi hàm

T là tham số của khuôn mẫu, trong khi a, b là tham số của hàm

Ví dụ 2: template <typename Containter, typename Object>

void push(Containter& s, Object o) {...}

Có thể dùng từ khoá “class” thay vì “typename” template <class T>

void swap(T& a, T& b) {...}

4

Page 5: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Gọi hàm khái quát

Gọi với kiểu tường minh: max<int>(a, b);

max<double>(x, y);

swap<String&>(s1, s2);

swap<Worker*&>(p1, p2);

push<List&, Student>(l, st);

Gọi với kiểu ngầm định: int a, b;

double x, y;

max(a, b); // max<int>(a, b);

max(x, y); // max<double>(x, y);

max(a, x); // lỗi

5

Page 6: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Chồng hàm khái quát

Các khuôn mẫu hàm cũng có thể được định nghĩa

chồng template <typename T> T max(T a, T b) { ... }

template <typename T> T max(T a, T b, T c) { ... }

template <typename T> T max(T* arr, int n) { ... }

Gọi hàm chồng max<int>(10, 20);

max('c', 'f');

max<double>(1.5, 2.1, 3.14);

max("1un34k", 6);

6

Page 7: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Cá biệt hoá hàm khái quát

Có thể định nghĩa các phiên bản cho trường hợp riêng

của một hàm khái quát template<typename T> T max(T a, T b) {

return a>b ? a:b; }

template<> const char*

max(const char* s1, const char* s2) {

return strcmp(s1, s2) == 1 ? s1:s2; }

Cá biệt hoá không hoàn toàn template <typename Containter, typename Object>

void push(Containter& s, Object o) {...}

template <typename Object>

void push(Stack& s, Object o) { ... }

7

Page 8: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Không chỉ khái quát hoá kiểu của tham số…

Khái quát hoá kiểu trả về template <typename Worker, typename Product>

Product makeProd(Worker& w) {

w.work();

return w.getResult();

}

Khái quát hoá kiểu của biến cục bộ template <typename List, typename Iterator>

void forEach(const List& l) {

Iterator i = l.first();

for (; i!=l.last(); i = i.next())

doSmth(i.get());

}

8

Page 9: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Tham số của khuôn mẫu không chỉ là kiểu...

Có thể là các giá trị sử dụng giá trị đó như hằng template<typename Object, int N>

Object* makeArray() {

return new Object[N]; }

string* p1 = makeArray<string, 5>();

SinhVien* p2 = makeArray<SinhVien, 10>();

Cả giá trị và kiểu cùng là tham số template<typename T, T min, T max>

T range(T t) {

return t<min ? min :

(t>max ? max : t);

}

y = range<double, -1.5, 2.>(x);

b = range<int, 10, 20>(a);

9

Page 10: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Lưu ý khi dùng các hàm khái quát

Phần thực thi của các khuôn mẫu hàm chỉ thực sự được biên dịch

khi có thông tin về kiểu

nếu viết khuôn mẫu hàm trong thư viện thì cả nguyên mẫu và

phần thực thi của hàm đều viết trong file .h

(có thể viết riêng phần thực thi ra một file khác rồi include vào file .h)

Khi sử dụng tham số thuộc kiểu gì thì trình biên dịch mới sinh ra hàm

tương ứng với kiểu tham số đó int a = 10, b = 20;

swap(a, b); // sinh ra: void swap(int&, int&) {...}

swap<float>(x, y); // void swap(float&, float&) {...}

Có thể khi dùng ở trường hợp cụ thể mới phát sinh lỗi cú pháp template <typename T> T divide(T a, T b)

{ return a/b; }

double z = divide(1.5, 0.5); // OK

const char* c = divide("ssss", "dddd"); // lỗi

10

Page 11: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Khuôn mẫu lớp

(Class templates)

11

Page 12: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Khái niệm

Tương tự với hàm, các lớp cũng có thể được khái quát

hoá khuôn mẫu lớp

Lớp được khai báo sử dụng những kiểu chưa xác định

và được tham số hoá template <typename Object>

class Array {

private:

int N;

Object* p;

public:

void setAt(int i, Object o) {...}

Object& operator[](int i) {...}

...

};12

Page 13: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Phương thức bên ngoài khuôn mẫu lớp

13

template<typename Object>

class Array {

private:

int N;

Object* p;

public:

Array(int N);

~Array();

void setAt(int i, Object o);

int length() const;

Object& operator[](int i);

};

template<typename Object>

Array<Object>::Array(int N) {

this->N = N;

p = new Object[N]; }

template<typename Object>

Array<Object>::~Array()

{ delete[] p; }

template<typename Object>

void Array<Object>::

setAt(int i, Object o)

{ p[i] = o; }

template<typename Object>

int Array<Object>::length() const

{ return N; }

template<typename Object>

Object& Array<Object>::

operator[](int i)

{ return p[i]; }

Các phương thức ở ngoài được khai báo tương tự như các khuôn

mẫu hàm

Page 14: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Sử dụng đối tượng của lớp khái quát

Ví dụ: Array<double> a(10);

for (int i=0; i<10; i++)

a.setAt(i, i*2);

typedef Array<string> StrArray;

StrArray s(2);

s[0] = string("abcd");

s[1] = string("12345");

Kết hợp khuôn mẫu lớp và hàm template<typename Object>

void printArray(Array<Object> &a) {

for (int i=0; i<a.length(); i++)

cout << a[i];

}

printArray(s);

14

Page 15: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Hàm bạn và lớp bạn

Bạn riêng của từng lớp tương ứng template<typename T>

class Array {

...

friend void sort(Array<T>& a);

friend class Serializer<T>;

};

Bạn chung của tất cả các lớp template<typename T>

class A {

...

friend void func1();

template<typename X> friend int func2();

friend class B;

};

15

Page 16: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Cá biệt hoá lớp khái quát

template<typename Key, typename Data> class Map {

...

Data find(Key k);

};

// cá biệt hoá hoàn toàn

template<> class Map<int, int> {

...

int find(int k);

};

// cá biệt hoá không hoàn toàn

template<typename Data> class Map<int, Data> {

...

Data find(int k);

};

16

Page 17: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Giá trị mặc định của tham số khuôn mẫu

Các tham số của khuôn mẫu lớp có thể có giá trị mặc

định template<typename Object = int, int N = 100>

class Pool {

...

};

Pool<> p1; // Pool<int, 100> p1;

Pool<string> p2; // Pool<string, 100> p2;

Pool<double, 20> p3;

Chú ý: chỉ khuôn mẫu lớp mới có giá trị mặc định của

tham số, khuôn mẫu hàm không có(*)

17

* Tuy nhiên, đã được bổ sung trong C++11

Page 18: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Biến static của lớp khái quát

Mỗi lớp được sinh ra từ khuôn mẫu có biến static riêng

template<typename Data>

class smartptr {

...

static smartptr<Data> nullptr;

};

template<typename Data>

static smartptr<Data> smartptr<Data>::nullptr;

smartptr<string> p1 = smartptr<string>::nullptr;

smartptr<double> p2 = smartptr<double>::nullptr;

Tương tự với các phương thức static

18

Page 19: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Các lớp khái quát liên quan template<typename T> class Iterator;

template<typename T> class List {

...

Iterator<T> begin() { ... }

Iterator<T> end() { ... }

};

template<typename T> class Iterator {

...

T& getData() { ... }

Iterator<T> next() { ... }

};

List<float> l;

...

Iterator<float> i;

for (i = l.begin(); i != l.end(); i = i.next())

cout << i.getData() << endl;

19

Page 20: Chapter 11: Lập trình khái quát Generic programming. C++ - Generic Programming.pdfEE3490: Kỹthuậtlậptrình –HK1 2018/2019 TS. ĐàoTrung Kiên –ĐH Bách khoa Hà Nội

EE3490: Kỹ thuật lập trình – HK1 2018/2019

TS. Đào Trung Kiên – ĐH Bách khoa Hà Nội

Bài tập

1. Viết hàm nhập dữ liệu cho mảng có kiểu bất kỳ

2. Viết hàm sắp xếp một mảng bất kỳ theo 2 cách: (C) sử dụng một

hàm so sánh làm cơ sở cho thứ tự sắp xếp, (C++) dùng templatevoid sortC(void* a, int n, int size,

int (*compare)(void*, void*));

template<typename T> void sortCpp(T* a, int n);

3. Viết lớp Stack cho phép chứa dữ liệu kiểu bất kỳ mà không cần

dùng con trỏ void*

4. Sửa lại lớp DSLK cho phép chứa dữ liệu kiểu bất kỳ

5. Sửa lại lớp Iterator cho DSLK ở trên

6. Sửa lớp Vector cho phép làm việc với cả dữ liệu float và double

7. Sửa lớp String cho phép làm việc với cả chuỗi ASCII (char) và

Unicode (wchar_t)

20