版本8和9间的区别
于2006-07-27 20:01:19修订的的版本8
大小: 20794
编辑: czk
备注:
于2006-07-28 17:16:47修订的的版本9
大小: 21575
编辑: czk
备注:
删除的内容标记成这样。 加入的内容标记成这样。
行号 191: 行号 191:
struct Clock {
    int second, minute, hour;
    inline void ShowTime();
};
行号 217: 行号 221:
成员函数也可以有缺省参数 成员函数也可以有缺省参数。缺省值写在声明中,而不是定义中。
行号 224: 行号 228:
    hour = h; minute = m; second = s;     hour = h;
    
minute = m;
    
second = s;
行号 242: 行号 248:
    d.SetTime(8, 27, 0); // 访问public, ok
    d.hour = 10; // 访问private错误
    d.SetTime(8, 27, 0); // 类内访问public, ok
    d.hour = 10; // 类内访问private错误
行号 246: 行号 252:
    hour = h; //访问private, ok     hour = h; //类内访问private, ok
行号 293: 行号 299:
}}}

'''并非'''所有数据成员都是private,所有成员函数都是公有的。
{{{#!cplusplus
class Clock {
private: //只能在类内访问
    int hour, minute, second;
public: //可以在类外访问
    void Add(int s) {
        for(int i = 0; i < s; i++)
            inc();
    }
private:
    void inc() {
        second++;
        if(second == 60) {
            second = 0;
            minute ++;
            if(minute == 60) {
                minute = 0;
                hour++;
                if(hour == 24)
                    hour = 0;
            }
        }
    }
};

C++类与对象

TableOfContents

1. 问题

C++语言只提供了整数、浮点数、bool、字符等基本类型。如何处理系统没有内置的类型?比如复数?时间?日期?坐标?传统C语言的做法是使用结构体,比如:

   1 struct complex {
   2     double real;
   3     double imag;
   4 };
   5 complex add(complex a, complex b); 
   6 complex substract(complex a, complex b);
   7 complex multiply(complex a, complex b);
   8 
   9 int main() {
  10     complex x, y;
  11     x.real = 10.0;
  12     x.imag = 20.0;
  13     y.real = 1.0;
  14     y.imag = -2.0;
  15     complex z = add(x, y);
  16 }

另一个例子:

   1 struct Clock {
   2     int second;
   3     int minute;
   4     int hour;
   5 };
   6 void SetTime( Clock *c, int h, int m, int s) {
   7     c->second = s;
   8     c->minute = m;
   9     c->hour = h;
  10 }
  11 void ShowTime( Clock *c) {
  12     cout << c->hour << c->minute << c->second;
  13 }

再一个例子:

   1 struct Student{
   2    char num[20];
   3    char name[10];
   4    bool gender;
   5 };
   6 void display(Student &s) {
   7     cout << s.num << s.name << (s.gender?"male":"female");
   8 } 
   9 int main() {
  10     Student stud1, stud2;
  11     // initialize
  12     display(stud1);
  13     display(stud2);
  14 }

2. C++的类

C++的做法是在C语言的基础上更进一步,将数据与操作这组数据的函数结合在一起,构成类(class)

   1 struct Clock {
   2     int  hour, minute, second;
   3     void SetTime(int h, int m, int s);
   4     void ShowTime();
   5 };

C++中类的定义可以用struct或者用class。函数与数据结合在一起,逻辑关系更加明确。类中的函数又被称为方法、成员函数。C++类中函数的定义,函数体可以直接写在类的内部,写在头文件中:

   1 struct Clock {
   2     int  hour, minute, second;
   3     void SetTime(int h, int m, int s) {
   4         hour = h; minute = m; second = s;
   5     }
   6     void ShowTime() {
   7         cout << hour << minute << second;
   8     }
   9 };
  10 
  11 int main() {
  12     Clock c1, c2;
  13     c1.SetTime(10, 10, 30);
  14     c2.SetTime(18, 00, 00);
  15     c1.ShowTime();
  16     c2.ShowTime();
  17 }

也可以分开定义,比如:

   1 //clock.h头文件:
   2 struct Clock {
   3     int  hour, minute, second;
   4     void SetTime(int h, int m, int s);
   5     void ShowTime();
   6 };
   7 
   8 //clock.cpp源文件:
   9 void Clock::SetTime(int h, int m, int s) {
  10     hour = h;
  11     minute = m;
  12     second = s;
  13 }
  14 void Clock::ShowTime() {
  15     cout << hour << minute << second;
  16 }
  17 
  18 // main.cpp源文件
  19 int main() {
  20     Clock c1, c2;
  21     c1.SetTime(10, 10, 30);
  22     c2.SetTime(18, 00, 00);
  23     c1.ShowTime();
  24     c2.ShowTime();
  25 }

另一个例子:

   1 struct Student{
   2    char num[20];
   3    char name[10];
   4    bool gender;
   5    void diplay() {
   6        cout << num << name << (gender?"male":"female");
   7    }
   8 };
   9 int main() {
  10    Student stud1, stud2;
  11    // initialize
  12    stud1.display();
  13    stud2.display();
  14 }

对象的定义和访问方式类似于结构体

   1 int main() {
   2    struct Clock now;      // 类似于C语言结构体
   3    Clock next;            // struct可以省略
   4    next.hour = 0;         // 同C语言结构体类似,可以访问成员
   5    now.SetTime(9, 40, 0); // 可以用类似的方式调用成员函数
   6    now.ShowTime();
   7    next.SetTime(9, 40, 1);
   8    next.ShowTime();
   9 }

在堆空间分配和访问对象

   1 void f() {
   2     Clock * my_clock = new Clock; //分配对象
   3     my_clock->hour = 10;          //通过指针访问成员
   4     my_clock->SetTime (9, 40, 0); //通过指针调用成员函数
   5     my_clock->ShowTime();
   6     delete my_clock;              //释放对象
   7 }

定义对象数组

   1 int main() {
   2     Clock clocks[100]; // 类似于int array[100];
   3     clocks[0].SetTime(9, 10, 25);
   4     clocks[1].SetTime(9, 9, 13);
   5     clocks[2].SetTime(9, 12, 25);
   6     // ... ...
   7     Clock *pc = new Clock[100];
   8     pc[0].SetTime(11, 20, 30);
   9     // ... ...
  10 }

成员函数和普通函数一样可以内联。函数体写在类定义内部的成员函数,默认就是内联的。

   1 struct Clock {
   2     int second, minute, hour;
   3     inline void ShowTime() { //inline may be omitted
   4         cout << hour << minute << second;
   5     }
   6 };

类外定义函数体的成员函数,要定义成内联需要加inline关键字,并把函数体写在头文件中

   1 struct Clock {
   2     int second, minute, hour;
   3     inline void ShowTime();
   4 };
   5 inline void Clock::ShowTime() {
   6     cout << hour << minute << second;
   7 }

成员函数也可以重载

   1 struct Clock {
   2     int second, minute, hour;
   3     void SetTime(int h, int m) {
   4         hour = h;
   5         minute = m;
   6         second = 0;
   7     }
   8     void SetTime(int h) {
   9         hour = h;
  10         minute = second = 0;
  11     }    
  12 };
  13 int main() {
  14     Clock t;
  15     t.SetTime( 10, 30);
  16     t.SetTime( 10 );
  17 }

成员函数也可以有缺省参数。缺省值写在声明中,而不是定义中。

   1 struct Clock {
   2     int second, minute, hour;
   3     void SetTime(int h =0, int m=0, int s=0);
   4 };
   5 void Clock::SetTime(int h, int m, int s) {
   6     hour = h;  
   7     minute = m; 
   8     second = s;
   9 }

3. 类的访问权限

   1 class Clock {
   2 private: //只能在类内访问
   3     int hour, minute, second;  
   4 public:  //可以在类外访问
   5     void SetTime(int yy, int mm, int dd);
   6     void ShowTime();
   7 };

公有部分定义了类的外部接口,可供类的使用者调用。私有部分隐藏了类的具体实现,由类的实现者实现,不需要使用者关心。这就是封装。private和public为新增的关键字。

   1 int main(){
   2     Clock d;
   3     d.SetTime(8, 27, 0);  // 类内访问public, ok
   4     d.hour = 10;          // 类内访问private错误
   5 }
   6 void Clock::SetTime(int h, int m, int s) {
   7     hour = h;    //类内访问private, ok
   8     minute = m;
   9     second = s;
  10 }

struct与class的访问权限的区别:struct默认public,class默认为private

   1 struct ClockA {
   2     void SetTime(int yy, int mm, int dd); // public here
   3 };
   4 
   5 class ClockB {
   6     int hour, minute, second; // private here;
   7 };

另一个例子:

   1 class Student{
   2 private:
   3     char num[20];
   4     char name[10];
   5     bool gender;
   6 public:
   7     void display() {
   8         cout << num << name << (gender?"male":"female");
   9     }
  10     void setnum(char n[]) {
  11         strcpy(num, n);
  12     }
  13     void setname(char n[]) {
  14         strcpy(name, n);
  15     }
  16     void setgender(bool g) {
  17         gender = g;
  18     }
  19 };
  20 
  21 int main() {
  22     Student stud1;
  23     stud1.setname("jack");
  24     stud1.setnum("05020001");
  25     stud1.setgender(true);
  26     stud1.display();
  27     cout << stud1.gender;  //error
  28 }

并非所有数据成员都是private,所有成员函数都是公有的。

   1 class Clock {
   2 private: //只能在类内访问
   3     int hour, minute, second;  
   4 public:  //可以在类外访问
   5     void Add(int s) {
   6         for(int i = 0; i < s; i++) 
   7             inc();
   8     }
   9 private:
  10     void inc() {
  11         second++;
  12         if(second == 60) {
  13             second = 0;
  14             minute ++;
  15             if(minute == 60) {
  16                 minute = 0;
  17                 hour++;
  18                 if(hour == 24)
  19                     hour = 0;
  20             }
  21         }
  22     }
  23 };

4. 构造与析构

定义变量的同时完成初始化(resource acquisition is initialization)是一种好的编程习惯,可以避免错误。

   1 int i = 5;  // 定义普通变量,定义的同时初始化
   2 cout << i;  // 访问变量
   3 Clock time; // 定义对象,定义了以后没有初始化
   4 time.ShowTime();          //错误,没有初始化
   5 time.SetTime(2000, 5, 1); //需要调用初始化函数后
   6 time.ShowTime();          //才能够访问
   7 

为避免偶然使用没有初始化的对象的错误,可以在类中定义一个特殊的函数——构造函数。它在对象被定义时,就被自动调用,以确保完成初始化。

   1 class Clock{
   2 public:
   3     Clock(int h, int m, int s);
   4 };

构造函数的名字与类名相同,没有返回类型。它利用特定的值构造对象,把对象初始化为一个特定的状态。

   1 Clock::Clock( int h, int m, int s) {
   2     hour = h; minute = m; second = s;
   3 }

带构造函数的对象定义

   1 int main() {
   2     Clock now;  //错误
   3     Clock getup(6, 30, 30); 
   4     Clock now = Clock(9, 21, 20);
   5     Clock *pC = new Clock(23, 30, 25);
   6 }

构造函数可以重载,也可以有缺省参数:

   1 class Clock {
   2     int hour, minute, second;
   3 public:
   4     Clock(int h, int m=0, int s=0);
   5     Clock(); // default constructor默认构造函数
   6     Clock(const char *);
   7     void Clock (/*...*/); // error
   8 };

各个构造函数所需要参数不同,但构造函数都没有返回类型。在定义对象时,会根据传递的参数来选择一个特定的构造函数初始化对象。

   1 int main() {
   2     Clock now;  //默认构造now对象call default constructor
   3     Clock getup(6, 30, 30); 
   4     Clock now = Clock(9, 21);
   5     Clock *pC = new Clock("23:30:0");
   6 }

一个类要能够默认构造对象,需要:这个类一个构造函数都没有写(系统会自动生成一个默认构造函数),或者至少写有一个默认构造函数。一个所有参数具有默认参数的构造函数也是默认构造函数,比如

   1 class Clock {
   2     int hour, minute, second;
   3 public:
   4     Clock(int h =0, int m = 0, int s=0);
   5 };

对象被摧毁时,一个成员函数也会自动被调用,这个函数称为称为析构函数,一般完成资源的释放工作

   1 class Clock {
   2         int hour, minute, second;
   3 public:
   4         ~Clock();
   5 };

析构函数的名字为类名前加~,没有返回类型和参数。析构函数不能重载。例如:

   1 int main() {
   2     Clock d1(4, 11, 1);
   3     if( true ){
   4         Clock *d2 = new Clock(9, 9, 9);
   5         Clock d3(8, 10,0);
   6         //…
   7     } // d3 is destructed here
   8     delete d2; // d2 is destructed here
   9     Clock d4;
  10 }  // d1,d4 is destructed here
  11 

new会自动调用构造函数,而malloc不能。delete会自动调用析构函数,而free不能。

   1 int main() {
   2     Clock *d1 = new Clock ;
   3     Clock *d2 = new Clock ( 10, 11, 1);
   4     Clock *d3 = new Clock [ 1000 ];
   5     Clock *d4 = (Clock *)malloc( sizeof(Clock) );
   6     delete d1;
   7     delete d2;
   8     delete[ ] d3;
   9     free(d4);
  10 }

要定义对象数组,并且没有初始化,那么要求该类可以默认构造对象。比如:

   1 void f() {
   2     Clock cls[10];
   3     Clock *p = new Clock[10];
   4 }

5. 对象的拷贝构造

对象在两种情况下发生复制:赋值和拷贝构造。拷贝构造是指创建一个对象,创建的同时让它的值与另一个已存在的对象一模一样。赋值是复制另一个同类型的值给已存在的对象,使它变成新的值。比如

   1 void f( Date d ); 
   2 
   3 int main() {
   4     Date today;      //默认构造函数
   5     Date d = today;  //拷贝构造
   6     Date s( today ); //拷贝构造
   7     d = another_day; //赋值
   8     f(s);            //拷贝构造
   9 

拷贝构造的工作由拷贝构造函数完成。如果自己没有编写拷贝构造函数,编译器会自动生成一个拷贝构造函数,用于完成缺省的拷贝构造的功能:用源对象的所有数据成员逐一拷贝构造目标对象的相应成员。我们可以自定义拷贝构造函数去改写默认的拷贝构造函数。

   1 class Clock {
   2     int hour, minute, second;
   3 public:
   4     Clock(Clock& c) {
   5          hour = c.hour; 
   6          minite = c.minute;
   7          second = 0;
   8     }
   9 };

何时需要自定义拷贝构造函数、析构函数?当涉及到内存管理时,一个类中有指针成员,指向动态分配的空间,那么通常需要写一组拷贝构造函数、析构函数和赋值操作符。写一个类Array,模拟数组的功能,元素是int类型,数组的大小可以是变量,增加检查越界的功能。即可以这样使用:

   1 int main() {
   2     Array arr(10);  // 相当于int arr[10];
   3     arr.at(0) = 3;  // 相当于arr[0] = 3;
   4     arr.at(1) = 5;  // 相当于arr[1] = 5;
   5 }

   1 class Array {
   2 private:
   3     int *p;
   4     int size;
   5 public:
   6     Array(int s) {
   7         size = s;
   8         p = new int [ size ];
   9     }
  10     ~Array() {
  11         delete[ ] p; 
  12     }
  13     int & at( int i) {
  14         if( i < size)
  15             return p[i];
  16         else
  17             return 0; 
  18     }
  19 };

但是这个类在这样使用时存在问题:

   1 int main() {
   2     Array arr( 10 );
   3     arr.at(0) = 10;
   4     Array arr2(arr); // error here
   5 }

拷贝构造的对象与原对象共享同一块空间,修改了一个对象,另一个对象也受影响。当一个对象释放时,另一个对象也不能使用了。当两个对象都被释放时,同一个空间释放了两次。解决办法是自定义拷贝构造函数:

   1 class Array {
   2 private:
   3     int *p;
   4     int size;
   5 public:
   6     Array(int s) {
   7         size = s;
   8         p = new int [ size ];
   9     }
  10     ~Array() {
  11         delete[ ] p;
  12     }
  13     int & at( int i) {
  14         if( i < size)
  15             return p[i];
  16         else
  17             return 0;
  18     }
  19     Array( Array &a) { 
  20         size = a.size;
  21         p = new int [size];
  22         for(int i = 0; i < size; i++)
  23             p[i] = a.p[i];
  24     }
  25 };

6. 类的组合

构造复杂的对象的一种方法是组合。一个类可以使用另一个类的对象作为成员,比如:

   1 class point{
   2 private:
   3     double x, y;
   4 public:
   5     //...
   6 };
   7 
   8 class line {
   9 private:
  10     point start, end;
  11 public:
  12     //...
  13 };
  14 
  15 class circle {
  16 private:
  17     point center;
  18     double radius;
  19 public:
  20     //...
  21 };
  22 
  23 class rectangle {
  24 private:
  25     point p1, p2;
  26 public:
  27     //...
  28 };

组合对象的构造

   1 class point{
   2 public:
   3     double x, y;
   4     point(double x0 = 0, double y0 = 0) {
   5         x = x0;
   6         y = y0;
   7         cout << "point " << x << y << "initialized!" << endl;
   8     }
   9 };
  10 
  11 class line {
  12 public:
  13     point start, end;
  14     line(double x0, double y0, double x1, double y1) {
  15         cout << "line initializing" << endl;
  16         start.x = x0;
  17         start.y = y0;
  18         end.x = x1;
  19         end.y = y1;
  20     }
  21 };

从这个程序的运行结果可以看出,在line类的构造函数执行前,start和end对象已经构造完成。point类没有默认构造函数,line不能构造。如果x、y是私有的,那么在line构造函数中不能对他们进行赋值。解决办法:使用构造函数初始化列表

   1 class point{
   2 private:
   3     double x, y;
   4 public:
   5     point(double x0, double y0) {
   6         x = x0;
   7         y = y0;
   8         cout << "point " << x << y << "initialized!" << endl;
   9     }
  10 };
  11 
  12 class line {
  13 private:
  14     point start, end;
  15 public:
  16     line(double x0, double y0, double x1, double y1)
  17      : start(x0, y0), end(x1, y1) {
  18         cout << "line initializing" << endl;
  19     }
  20 };

在line构造函数的初始化列表中,给start、end成员的构造函数传递参数完成它们的初始化。

初始化表还能够解决其它一些类型的成员的初始化问题,比如const成员,引用成员等。比如

   1 class C {
   2 public:
   3     C(int h, int m, int s) 
   4         : s ( 0 ), x( 0 ), time(h, m, s), t(time) 
   5     {   
   6     }
   7 private:
   8     int x;
   9     const int s;  //const data member
  10     Clock time;   // no default construct
  11     Clock &t;     // reference member
  12 };

构造函数和析构函数执行的顺序

   1 class point{
   2 private:
   3     double x, y;
   4 public:
   5     point(double a, double b) :x(a), y(b) { 
   6         cout << "construct point" << endl; 
   7     }
   8     ~point() {
   9         cout << "destruct point" << endl;
  10     }
  11 };
  12 class line {
  13 private:
  14     point start, end;
  15 public:
  16     line(double x0, double y0, double x1, double y1) 
  17         : start(x0, y0) , end(x1, y1) {
  18         cout << "construct line" << endl;
  19     }
  20     ~line() {
  21         cout << "destruct line" << endl;
  22     }
  23 };

7. 静态成员

C++的类有一种特殊的成员,它的空间是这个类的所有对象共享的,称为类静态数据成员

   1 class Task {
   2 public:
   3     static unsigned n;
   4     int m;
   5 };
   6 unsigned Task::n;
   7 int main() {
   8     Task  t1, t2;
   9     t1.n = 10;
  10     cout << t2.n;
  11 }

静态成员变量的定义、初始化和访问

   1 class Task {
   2 public:
   3     Task() : x(n) { n++; }
   4     ~Task() { n--; }
   5     int x;
   6     static unsigned n;
   7 };
   8 unsigned Task::n = 0;
   9 
  10 int main() {
  11     Task s1;
  12     cout << s1.x << s1.n << endl;
  13     Task s2;
  14     cout << s1.x << s1.n << endl;
  15     cout << s2.x << s2.n << endl;
  16     cout << Task::n << endl;
  17 }

静态成员函数:只能访问静态成员的成员函数

   1 class Task {
   2 private:
   3     static unsigned n;
   4 public:
   5     static unsigned get_count() {
   6         return n;
   7     }
   8 };
   9 unsigned Task::n = 0;
  10 int main() {
  11     cout << Task::get_count();
  12     Task t;
  13     cout << t.get_count();
  14 }

静态成员用法举例:写一个类Singleton,这个类只能存在一个对象。

   1 class Singleton{
   2 public:
   3     static  Singleton &create() {
   4         static Singleton s;
   5         return s; 
   6     }
   7 private:
   8     Singleton() { /*...*/ }
   9     Singleton(Singleton&){ /*...*/ }
  10 };

8. 友元

友元提供了让其它函数或类直接访问私有成员的途径。友元函数是能够直接访问类的私有成员的函数。

class Point {
private:
    int x, y;
public:
    int GetX() { return x; }
    friend bool isequal(Point a, Point b);
};
bool isequal(Point a, Point b) {
    return a.x == b.x && a.y == b.y;
}

友元类

   1 class Point {
   2     int x, y;
   3     friend class Rectangle; // Rectangle是Point的友元类
   4 };
   5 class Rectangle {
   6     Point p1, p2;
   7 public: 
   8     void display() {
   9          cout << p1.x << p1.y << p2.x << p2.y;
  10     }
  11 };

友元关系是单向的,并不可传递。

== const对象 ==

定义对象前面加const,表示对象不能改变

   1 class Clock{
   2     int hour, minute, second;
   3 public:
   4     Clock(int h, int m, int s)
   5        : hour(h), minute(m), second(s)
   6     {
   7     }
   8     void SetTime(int h, int m, int s) {
   9         hour = h;
  10         minute = m;
  11         second = s;
  12     }
  13     void ShowTime() {
  14         cout << hour << minute << second;
  15     }
  16 };
  17 int main() {
  18     const int i = 10;
  19     i++;                        //error!
  20     const Clock  t(10, 30, 20);
  21     cout << t.second;
  22     t.second = 10;              // error
  23     t.SetTime(10, 20, 10);      // error!
  24     t.ShowTime();               // error!
  25 }

成员函数声明后面加const,表示这个成员函数不修改数据成员。这样的函数可以被const对象调用。

   1 class Clock {
   2     int second, minute, hour;
   3 public:
   4     void ShowTime() const {
   5         cout << second << minute << hour ;
   6         hour = 10; // error!
   7     }
   8 };

常成员函数可以被常对象调用,也可以被普通对象调用。

   1 int main() {
   2    const  Clock  time(10, 20, 30);
   3    time.ShowTime();
   4    Clock now(10, 10, 10);
   5    time.ShowTime();
   6 }

常成员函数与非常成员函数可以重载

   1 class Array {
   2 private:
   3     int *p, size;
   4 public:
   5     Array(int s) {
   6         size = s;
   7         p = new int [ size ];
   8     }
   9     ~Array() { delete[ ] p; }
  10     int & at( int i) {
  11          if( i < size) return p[i]; else throw out_of_range(); 
  12     }
  13     int at( int i) const  {
  14          if( i < size) return p[i]; else throw out_of_range(); 
  15     }
  16 };
  17 int main() {
  18     Array a(10);
  19     a.at(5) = 5;
  20     const Array b(5);
  21     b.at(5) = 5;      // error
  22 }

9. this指针

   1 class Clock {
   2     int second, minute, hour;
   3 public:
   4         void ShowTime() {
   5         cout <<  second << minute << hour;
   6       //cout << this->second << this->minute << this->hour;
   7     }
   8 };
   9 int main() {
  10     Clock  t1(10, 20, 30), t2(20, 30, 10);
  11     t1.ShowTime();
  12     t2.ShowTime();
  13 }

   1 void f() {
   2     Clock time(1999, 9, 9);
   3     time.set_second(20);
   4     time.set_minute(30)
   5     time.set_hour(10);
   6     time.set_second(20).set_minute(30).set_hour(10);
   7 }
   8 class Clock {
   9 public:
  10         Clock &set_second(int n);
  11         Clock &set_minute(int n);
  12         Clock &set_hour(int n);
  13 };
  14 Clock &Clock::set_second(int n) {
  15         second = n;
  16         return *this;
  17 }

this有两种类型:以Clock类为例,Clock* const和const Clock* const

   1 class Clock {
   2 public:
   3     void ShowTime() const {
   4         cout <<  second << minute << hour;
   5         second++; //error this类型为const Date *this
   6 
   7         Clock *p = (Clock*)(this); // 或者Clock *p = const_cast<Clock*>(this);
   8         p->second++; //ok
   9     }
  10 };

The End

C++类与对象 (2008-05-08 15:51:53由czk编辑)

ch3n2k.com | Copyright (c) 2004-2020 czk.