版本18和34间的区别 (跳过第16版)
于2006-10-11 22:20:21修订的的版本18
大小: 29369
编辑: czk
备注:
于2008-02-23 15:35:18修订的的版本34
大小: 28373
编辑: localhost
备注: converted to 1.6 markup
删除的内容标记成这样。 加入的内容标记成这样。
行号 3: 行号 3:
[[TableOfContents]] <<TableOfContents>>
行号 16: 行号 16:
void set(complex *c, double r, double i);
行号 19: 行号 20:
    x.real = 10.0;
    x.imag = 20.0;
    y.real = 1.0;
    y.imag = -2.0;
    set(&x, 10.0, 20.0);
    set(&y, 1.0, -2.0);
行号 43: 行号 42:
void inc_second(clock *c) {
    c->second ++;
    if(c->second == 60) {
        c->minute ++;
        c->second = 0;
        if(c->minute == 60) {
            c->hour ++;
            c->minute = 0;
            if(c->hour == 24)
                c->hour = 0;
        }
    }
}
行号 47: 行号 59:
    inc_second(&c);
行号 71: 行号 84:
    // initialize
行号 87: 行号 99:
    void inc_second();
行号 101: 行号 114:
    void inc_second(clock *c) {
        second ++;
        if(second == 60) {
            minute ++;
            second = 0;
            if(minute == 60) {
                hour ++;
                minute = 0;
                if(hour == 24)
                    hour = 0;
            }
        }
    }
行号 118: 行号 144:
    void inc_second();
行号 129: 行号 156:
void clock::inc_second() {
    second ++;
    if(second == 60) {
        minute ++;
        second = 0;
        if(minute == 60) {
            hour ++;
            minute = 0;
            if(hour == 24)
                hour = 0;
        }
    }
}

行号 273: 行号 315:
    void set_time(int yy, int mm, int dd);     void set_time(int h, int m, int s);
行号 281: 行号 323:
    d.set_time(8, 27, 0); // 类内访问public, ok
    d.hour = 10; // 类内访问private错误
    d.set_time(8, 27, 30); // 类内访问public, ok
    d.hour = 10;  // 类内访问private错误
行号 294: 行号 336:
    void SetTime(int yy, int mm, int dd); // public here     void set_time(int h, int m, int s); // public here
private:
    int hour, minute, second; //private here
行号 299: 行号 343:
public:
    void set_time(int h, int m, int s); // public here;
行号 306: 行号 352:
    char num[20];
    char name[10];
    string num;
    string name;
行号 313: 行号 359:
    void setnum(char n[]) {
        strcpy(num, n);
    }
    void setname(char n[]) {
        strcpy(name, n);
    void setnum(string n) {
        num = n;
    }
    void setname(char n) {
        name = n;
行号 325: 行号 371:
    Student stud1;     student stud1;
行号 334: 行号 380:
'''并非'''所有数据成员都是private,所有成员函数都是公有的。
{{{#!cplusplus
class Clock {
'''并非'''所有数据成员都必须是private,'''并非'''所有成员函数都必须是公有的。
{{{#!cplusplus
class clock {
行号 340: 行号 386:
    void Add(int s) {     void add(int s) {
行号 363: 行号 409:
定义变量的同时完成初始化(resource acquisition is initialization)是一种好的编程习惯,可以避免错误。
{{{#!cplusplus
int main() {
    int i = 5; // 定义普通变量,定义的同时初始化
    cout << i; // 访问变量
    Clock time; // 定义对象,定义的同时并没有初始化
    time.ShowTime(); //错误,没有初始化
    time.SetTime(2000, 5, 1); //需要调用初始化函数后
    time.ShowTime(); //才能够访问
}
}}}

为避免偶然使用没有初始化的对象的错误,可以在类中定义一个特殊的函数——构造函数。它在对象被定义时,就被自动调用,以确保完成初始化。
{{{#!cplusplus
class Clock{
变量定以后,没有初始化就使用是错误的。比如:
{{{#!cplusplus
int main() {
    int i; // 定义普通变量
    cout << i; // 访问变量,错误,没有初始化
    clock time; // 定义对象,定义的同时并没有初始化
    time.show_time(); //错误,没有初始化
    time.set_time(20, 55, 10); //需要调用初始化函数后
    time.show_time(); //才能够访问
}
}}}

定义变量的同时完成初始化(resource acquisition is initialization)是一种好的编程习惯,可以避免错误。对于普通变量可以这样:
{{{#!cplusplus
int main() {
    int i = 5; // 定义普通变量,并初始化
    cout << i; // 访问变量,ok
}
}}}

而对于类变量,我们也希望可以完成类似的初始化,比如:
{{{#!cplusplus
int main() {
    clock c1(10,10, 30); // 定义对象,定义的同时并且初始化
    clock c2 = clock(10, 10, 31); // 或者这样初始化
    c1.show_time();
}
}}}

要这样做可以在类中定义一个特殊的函数——'''构造函数'''。它在对象被定义时就被自动调用,以确保对象能够初始化。
{{{#!cplusplus
class clock{
行号 381: 行号 444:
    Clock(){
        hour = minute = second = 0;
    }
    void ShowTime() {
        cout << hour << minute << second;
    }
};
int main() {
    Clock c1; // 定义对象,定义的同时并且初始化
    c1.ShowTime();
}
}}}
这种没有参数的构造函数称作默认构造函数(default constructor)。

如果要在定义对象的同时将对象初始化成某一个给定的值,则可以给构造函数添加参数:
{{{#!cplusplus
class Clock{
    int hour, minute, second;
public:
    Clock(int h, int m, int s) {
    clock(int h, int m, int s){
行号 405: 行号 449:
    void ShowTime() {     void show_time() {
行号 409: 行号 453:

int main() {
    Clock c1(10, 10, 30);
    c1.ShowTime();
    Clock c2; //错误,缺少参数不能完成初始化
    c2.ShowTime();
}
}}}

构造函数也可以定义在类外:
{{{#!cplusplus
class Clock{
    int hour, minute, second;
public:
    Clock(int h, int m, int s);
};
Clock::Clock( int h, int m, int s) {
}}}

注意:构造函数是没有返回类型的,写成这样是语法错误:
{{{#!cplusplus
class clock{
public:
    void clock (/*...*/); // error
};
}}}

在定义了这样的构造函数以后,不使用参数来创建对象就变成了语法错误:
{{{#!cplusplus
int main() {
    clock c1; // 语法错误,没有这样的构造函数
    c1.show_time();
}
}}}

有时希望对象不需要参数也可以初始化,初始化到一种默认的状态,则可以用没有参数的构造函数,称作'''默认构造函数(default constructor)'''。

{{{#!cplusplus
class clock{
    int hour, minute, second;
public:
    clock() {
        hour = minute = second = 0;
    }
    void show_time() {
        cout << hour << minute << second;
    }
};

int main() {
    clock c1; // 初始化为0时0分0秒
    c1.show_time();
    clock c2(15, 30, 0) // 错误,没有这样的构造函数
    clock c3(); // 定义函数而不是定义对象
}
}}}

注意,要调用默认构造函数创建对象,不要在后面添加空的(),否则会被认为是函数声明,而不是对象定义。

如果要以多种不同的方式初始化对象,可以对构造函数进行重载。构造函数也可以有缺省参数:
{{{#!cplusplus
class clock {
    int hour, minute, second;
public:
    clock(int h, int m=0, int s=0);
    clock(); // default constructor默认构造函数
    clock(const char *s);
};
}}}
各个构造函数所需要参数不同。在定义对象时,会根据传递的参数来选择一个特定的构造函数初始化对象。
{{{#!cplusplus
int main() {
    clock now; //默认构造now对象call default constructor
    clock getup(6, 30, 30);
    clock early(6);
    clock sleep("23:30:0");
    clock lesson(9, 21);
}
}}}

一个所有参数具有默认参数的构造函数也是默认构造函数,比如
{{{#!cplusplus
class clock {
    int hour, minute, second;
public:
    clock(int h =0, int m = 0, int s=0);
};
int main() {
    clock c1;
}
}}}

构造函数和普通函数一样,也可以定义在类外:
{{{#!cplusplus
class clock{
    int hour, minute, second;
public:
    clock(int h, int m, int s);
};
clock::clock( int h, int m, int s) {
行号 432: 行号 542:
    Clock now; //错误
    Clock sleep(22, 30);
    Clock getup(6, 30, 30);
    Clock *pC = new Clock(23, 30, 25);
    Clock now = Clock(9, 21, 20);
}
}}}

构造函数初始化列表
{{{#!cplusplus
class Clock{
    int hour, minute, second;
public:
    Clock(int h, int m, int s)
     : hour(h), minute(m), second(s)
    {
    }
    void ShowTime() {
    clock sleep(22, 30, 0);
    clock getup(6, 30, 30);
    clock now = clock(9, 21, 20);
    clock *pC = new clock(23, 30, 25); //构造函数会被自动调用
}
}}}

在用new动态分配对象的时候,构造函数也会被自动调用。如果是用malloc分配内存,构造函数不会被自动调用。这是new和malloc最重要的区别。
{{{#!cplusplus
int main() {
    clock *pc = new clock(23, 30, 25); // 构造函数被掉用了
    clock *pd = (clock *)malloc(sizeof(clock)); //构造函数没有调用
}
}}}

要定义对象数组,并且没有初始化,那么要求该类可以默认构造对象。比如:
{{{#!cplusplus
class clock {
    int hour, minute, second;
public:
    clock(int h =0, int m = 0, int s=0) {
        hour = h;
        minute = m;
        second = s;
    }
};
void f() {
    clock cls[10];
    clock *p = new clock[10];
}
}}}

如果类的构造函数需要参数,则需要对数组进行初始化:
{{{#!cplusplus
class clock {
    int hour, minute, second;
public:
    clock(int h , int m, int s) {
        hour = h;
        minute = m;
        second = s;
    }
};
void f() {
    clock cls[10] = {clock(0,0,0), clock(1,0,0), clock(2,0,0), clock(3,0,0), clock(4,0,0), clock(5,0,0), clock(6,0,0), clock(7,0,0), clock(8,0,0), clock(9,0,0), clock(10,0,0)};
    clock *p = new clock[10]; // 错误,没有办法初始化
}
}}}

在前面写的构造函数中,我们对类的成员进行赋值,而不是初始化。在类的成员必须初始化而不能赋值的时候,这样做会带来问题。比如说类内有引用成员和const成员:
{{{#!cplusplus
class clock{
    const int hour, minute, second;
public:
    clock(int h, int m, int s) {
         hour = h; // 错误,const变量不能够赋值
         minute = m; // 错误
         second = s; // 错误
    }
    void show_time() {
行号 455: 行号 607:
初始化列表用在类中内嵌对象的情况:
{{{#!cplusplus
class WorldClock {
    Clock clock;
要对成员进行初始化,需要使用构造函数的初始化列表:
{{{#!cplusplus
class clock{
    const int hour, minute, second;
public:
    clock(int h, int m, int s)
        : hour(h), minute(m), second(s) {
    }
    void show_time() {
        cout << hour << minute << second;
    }
};
}}}

对于引用也要用同样的方式初始化。而对于普通变量也可以用初始化表进行初始化:
{{{#!cplusplus
class clock{
    int hour, minute, second;
public:
    clock(int h, int m, int s)
        : hour(h), minute(m), second(s) {
    }
    void show_time() {
        cout << hour << minute << second;
    }
};
}}}

在初始化列表里面要用()而不是=给出初始值。初始化列表还可以用在类中内嵌对象的情况:
{{{#!cplusplus
class world_clock {
    clock c;
行号 461: 行号 641:
    WorldClock(int hour, int minute, int second, int t)
        :clock(hour, minute, second), timezone(t) {
    }
};
}}}

构造函数可以重载,也可以有缺省参数:
{{{#!cplusplus
class Clock {
    int hour, minute, second;
public:
    Clock(int h, int m=0, int s=0);
    Clock(); // default constructor默认构造函数
    Clock(const char *s);
    void Clock (/*...*/); // error
};
}}}
各个构造函数所需要参数不同,但构造函数都没有返回类型。在定义对象时,会根据传递的参数来选择一个特定的构造函数初始化对象。
{{{#!cplusplus
int main() {
    Clock now; //默认构造now对象call default constructor
    Clock getup(6, 30, 30);
    Clock early(6);
    Clock *pC = new Clock("23:30:0");
    Clock now = Clock(9, 21);
}
}}}

一个类中一个构造函数都没有写,编译器会自动生成一个默认构造函数。
{{{#!cplusplus
class Clock {
    int hour, minute, second;
};

int main() {
    Clock c1; //没有初始化
}
}}}
自动生成的构造函数可以调用内嵌类型的默认构造函数(如果有默认构造函数的话)。
{{{#!cplusplus
class Student {
    string name;
    string num;
    bool gender;
public:
    void Show() {
        cout << name << num << gender;
    }
};

int main() {
    Student stud;
    stud.Show();
}
}}}

一个所有参数具有默认参数的构造函数也是默认构造函数,比如
{{{#!cplusplus
class Clock {
    int hour, minute, second;
public:
    Clock(int h =0, int m = 0, int s=0);
};
int main() {
    Clock c1;
}
}}}

对象被摧毁时,一个成员函数也会自动被调用,这个函数称为称为析构函数,一般完成资源的释放工作
{{{#!cplusplus
class Clock {
    int hour, minute, second;
public:
    ~Clock() {
    world_clock(int hour, int minute, int second, int t)
        :c(hour, minute, second), timezone(t) {
    }
};
}}}

如果一个类中一个构造函数都没有写,编译器会自动生成一个默认构造函数。自动生成的构造函数可以调用内嵌类型的默认构造函数(如果有默认构造函数的话)。
{{{#!cplusplus
class world_clock {
private:
    clock c;
    int timezone;
public:
};

int main() {
    world_clock wc; // wc.timezone没有初始化,但是wc.c的构造函数会自动调用
}
}}}

对象被摧毁时,另一个特殊的成员函数也会自动被调用,这个函数称为称为'''析构函数''',一般用来完成资源的释放工作。
{{{#!cplusplus
class clock {
    int hour, minute, second;
public:
    ~clock() {
行号 539: 行号 671:
行号 541: 行号 674:
int main() {
    Clock d1(4, 11, 1);

class clock {
    int hour, minute, second;
public:
    ~clock() {
        cout << "destruct:"<< hour << minute << second;
    }
};
int main() {
    clock d1(4, 11, 1);
行号 544: 行号 685:
        Clock *d2 = new Clock(9, 9, 9);
        Clock d3(8, 10,0);
        clock *d2 = new clock(9, 9, 9);
        clock d3(8, 10,0);
行号 549: 行号 690:
    Clock d4;     clock d4;
行号 555: 行号 696:
class String{ class mystring{
行号 558: 行号 699:
    String(char *str) {     mystring(char *str) {
行号 562: 行号 703:
    ~String() {     ~mystring() {
行号 567: 行号 708:
    String s("Hello");
}
}}}


new会自动调用构造函数,而malloc不能。delete会自动调用析构函数,而free不能。
{{{#!cplusplus
int main() {
    Clock *d1 = new Clock ;
    C
lock *d2 = new Clock ( 10, 11, 1);
    Clock *d3 = new Clock [ 1000 ];
    Clock *d4 = (Clock *)malloc( sizeof(Clock) ); // 没有初始化
    mystring s("Hello");
}
}}}


new会自动调用构造函数,而malloc不能。同样delete会自动调用析构函数,而free不能。
{{{#!cplusplus
int main() {
    clock *d1 = new clock ;
    c
lock *d2 = new clock ( 10, 11, 1);
    clock *d3 = new clock [ 1000 ];
    clock *d4 = (clock *)malloc( sizeof(clock) ); // 没有初始化
行号 586: 行号 727:
要定义对象数组,并且没有初始化,那么要求该类可以默认构造对象。比如:
{{{#!cplusplus
void f() {
    Clock cls[10];
    Clock *p = new Clock[10];
}
}}}
行号 598: 行号 731:
对象的复制分为两种情况:赋值和拷贝构造。拷贝构造是指创建一个新对象,创建的同时让新对象的值与另一个已存在的对象一模一样。赋值是复制一个对象的值给另一个已存在的对象,使两个对象的值一样。比如
{{{#!cplusplus
void display( Clock d ) {
对象的复制分为两种情况:'''赋值''''''拷贝构造''''''拷贝构造'''是指创建一个新对象,创建的同时让新对象的值与另一个已存在的对象一模一样。'''赋值'''是复制一个对象的值给另一个已存在的对象,使两个对象的值一样。比如
{{{#!cplusplus
void display( clock d ) {
行号 605: 行号 738:
    Clock now; //默认构造函数
    Clock d = now; //拷贝构造
    Clock s( now ); //拷贝构造
    clock now; //默认构造函数
    clock d = now; //拷贝构造
    clock s( now ); //拷贝构造
行号 610: 行号 743:
}
行号 615: 行号 748:
class Clock {
    int hour, minute, second;
public:
    Clock(int h, int m, int s) {
class clock {
    int hour, minute, second;
public:
    clock(int h, int m, int s) {
行号 624: 行号 757:
    Clock(Clock& c) {     clock(clock& c) {
行号 632: 行号 765:
    Clock c;
    Clock d(c); //拷贝构造d,其值与c一样。
    clock c;
    clock d(c); //拷贝构造d,其值与c一样。
行号 640: 行号 773:
class String{ class mystring{
行号 643: 行号 776:
    String(char *str) {     mystring(char *str) {
行号 647: 行号 780:
    ~String() {     ~mystring() {
行号 650: 行号 783:
    String(String &str) {     mystring(mystring &str) {
行号 656: 行号 789:
    String s("Hello");
    String s2(s);
}
}}}


再例如:写一个类Array,模拟数组的功能,元素是int类型,数组的大小可以是变量,增加检查越界的功能。即可以这样使用:
{{{#!cplusplus
int main() {
    Array arr(10); // 相当于int arr[10];
    mystring s("Hello");
    mystring s2(s);
}
}}}


再例如:写一个类array,模拟数组的功能,元素是int类型,数组的大小可以是变量,增加检查越界的功能。即可以这样使用:
{{{#!cplusplus
int main() {
    array arr(10); // 相当于int arr[10];
行号 668: 行号 801:
}
}}}
{{{#!cplusplus
class Array {
    arr.at(10) = 10; //error数组越界
}
}}}
{{{#!cplusplus
class array {
行号 676: 行号 810:
    Array(int s) {     array(int s) {
行号 680: 行号 814:
    ~Array() {     ~array() {
行号 695: 行号 829:
    Array arr( 10 );     array arr( 10 );
行号 697: 行号 831:
    Array arr2(arr); // error here     array arr2(arr); // error here
行号 703: 行号 837:
class Array { class array {
行号 708: 行号 842:
    Array(int s) {     array(int s) {
行号 712: 行号 846:
    ~Array() {     ~array() {
行号 721: 行号 855:
    Array( Array &a) {     array( array &a) {
行号 852: 行号 986:
    Clock time; // no default construct
    Clock &t; // reference member
    clock time; // no default construct
    clock &t; // reference member
行号 884: 行号 1018:
== 静态成员 ==
类的一般数据成员,每个对象都有这样一组数据成员,它们的空间是独立的。C++的类还有一种特殊的成员,它的空间是这个类的所有对象共享的,称为类静态数据成员
{{{#!cplusplus
class Task {
public:
    static unsigned n; //声明静态成员n
    int m;
};
unsigned Task::n; //定义静态成员n
int main() {
    Task t1, t2;
    t1.n = 10;
    cout << t2.n;
}
}}}
这里n的空间在整个程序里面只有一个,t1,t2共享同一个n。

静态数据成员需要声明和定义。初始化不是在构造函数中进行,而是在定义的时候进行。访问静态数据成员可以和一般成员一样,通过对象名来访问,也可以不通过对象直接用类名来访问。
{{{#!cplusplus
class Task {
public:
    Task() : x(n) { n++; }
    ~Task() { n--; }
    int x;
    static unsigned n;
};
unsigned Task::n = 1;// 初始化

int main() {
    cout << Task::n << endl;
    Task s1;
    cout << s1.x << s1.n << endl;
    Task s2;
    cout << s1.x << s1.n << endl;
    cout << s2.x << s2.n << endl;
    cout << Task::n << endl;
}

}}}


静态成员函数:只能访问静态成员(包括静态数据成员和静态成员函数)的成员函数
{{{#!cplusplus
class Task {
private:
    static unsigned n;
public:
    static unsigned get_count() {
        return n;
    }
};
unsigned Task::n = 0;
int main() {
    cout << Task::get_count();
    Task t;
    cout << t.get_count();
}

}}}

静态成员用法举例:写一个类Singleton,这个类只能存在一个对象。
{{{#!cplusplus
class Singleton{
public:
    static Singleton &instance() {
        static Singleton s;
        return s;
    }
private:
    Singleton() { /*...*/ }
    Singleton(Singleton&){ /*...*/ }
};
}}}

统计所有学生的平均成绩:
{{{#!cplusplus
class Student {
    string name;
    string num;
    float score;
    static float sum;
    static int count;
public:
    Student(string na, string nu, float sc)
        : name(na), num(nu), score(sc) {
        count ++;
        sum += score;
    }
    static float average() {
        return sum / count;
    }
};

float Student::sum = 0;
int Student::count = 0;

int main() {
    cout << Student::average();
    Student a("Jack", "0511001", 59);
    cout << a.average() << Student::average();
    Student b("Lisa", "0511002", 99);
    cout << a.average() << b.average() << Student::average();
}
}}}

== 友元 ==
友元提供了让其它函数或类直接访问私有成员的途径。友元函数是能够直接访问类的私有成员的函数。比如要写一个函数isequal比较两个点是否相同:
{{{#!cplusplus
class Point {
private:
    int x, y;
public:
    friend bool isequal(Point a, Point b);
};
bool isequal(Point a, Point b) {
    return a.x == b.x && a.y == b.y;
}

int main() {
    Point a, b;
    // initialize
    cout << isequal(a, b);
}
}}}
实际上不用友员也可以实现同样的功能,只要将x,y变成公有或者提供共有的函数访问他们就可以了:
{{{#!cplusplus
class Point {
private:
    int x, y;
public:
    int &get_x() { return x; }
    int &get_y() { return y; }
};
bool isequal(Point a, Point b) {
    return a.get_x() == b.get_x() && a.get_y() == b.get_y();
}

int main() {
    Point a, b;
    // initialize
    cout << isequal(a, b);
}
}}}


友元类:一个类是另一个类的友员,则一个类的所有成员函数都可以访问另一个类的所有私有成员
{{{#!cplusplus
class Point {
    int x, y;
    friend class Rectangle; // Rectangle是Point的友元类,Rectangle类的成员函数可以访问Point类的私有成员
};
class Rectangle {
    Point p1, p2;
public:
    void display() {
        cout << p1.x << p1.y << p2.x << p2.y;
    }
    bool in(Point p) {
        return p.x >= p1.x && p.x < p2.x && p.y >= p1.y && p.y < p2.y;
    }
};

int main() {
    Rectangle rect;
    // initialize
    rect.display();
}
}}}
注意:友元关系是单向的,并不可传递。
<<Include(C++:静态成员, "静态成员", 2)>>

<<Include(C++:友元, "友元", 2)>>
行号 1058: 行号 1026:
class Clock{
    int hour, minute, second;
public:
    Clock(int h, int m, int s)
class clock{
    int hour, minute, second;
public:
    clock(int h, int m, int s)
行号 1065: 行号 1033:
    void SetTime(int h, int m, int s) {     void set_time(int h, int m, int s) {
行号 1070: 行号 1038:
    void ShowTime() {     void show_time() {
行号 1078: 行号 1046:
    const Clock t(10, 30, 20);     const clock t(10, 30, 20);
行号 1081: 行号 1049:
    t.SetTime(10, 20, 10); // error!
    t.ShowTime(); // error!
    t.set_time(10, 20, 10); // error!
    t.show_time(); // error!
行号 1088: 行号 1056:
class Clock{
    int hour, minute, second;
public:
    Clock(int h, int m, int s)
class clock{
    int hour, minute, second;
public:
    clock(int h, int m, int s)
行号 1095: 行号 1063:
    void SetTime(int h, int m, int s) {     void set_time(int h, int m, int s) {
行号 1100: 行号 1068:
    void ShowTime() const {     void show_time() const {
行号 1108: 行号 1076:
    const Clock t(10, 30, 20);     const clock t(10, 30, 20);
行号 1111: 行号 1079:
    t.SetTime(10, 20, 10); // error!
    t.ShowTime(); // ok
    t.set_time(10, 20, 10); // error!
    t.show_time(); // ok
行号 1119: 行号 1087:
   const Clock time(10, 20, 30);
   time.ShowTime();
   Clock now(10, 10, 10);
   time.ShowTime();
   const clock time(10, 20, 30);
   time.show_time();
   clock now(10, 10, 10);
   time.show_time();
行号 1128: 行号 1096:
class Array { class array {
行号 1132: 行号 1100:
    Array(int s) {     array(int s) {
行号 1136: 行号 1104:
    ~Array() { delete[ ] p; }     ~array() { delete[ ] p; }
行号 1145: 行号 1113:
    Array a(10);     array a(10);
行号 1147: 行号 1115:
    const Array b(5);     const array b(5);
行号 1153: 行号 1121:
== this指针 ==
{{{#!cplusplus
class Clock {
    int second, minute, hour;
public:
    Clock(int h, int m, int s)
       : hour(h), minute(m), second(s)
    {
    }
    void ShowTime() {
        cout << second << minute << hour;
        //cout << this->second << this->minute << this->hour;
    }
};
int main() {
    Clock t1(10, 20, 30), t2(20, 30, 10);
    t1.ShowTime();
    t2.ShowTime();
}
}}}

{{{#!cplusplus

class Clock {
public:
 Clock &set_second(int n);
 Clock &set_minute(int n);
 Clock &set_hour(int n);
};
Clock &Clock::set_second(int n) {
 second = n;
 return *this;
}
int main() {
    Clock time(1999, 9, 9);
    time.set_second(20);
    time.set_minute(30)
    time.set_hour(10);
    time.set_second(20).set_minute(30).set_hour(10);
}
}}}

this有两种类型:以Clock类为例,Clock* const和const Clock* const
{{{#!cplusplus
class Clock {
public:
    void ShowTime() const {
        cout << second << minute << hour;
        second++; //error this类型为const Date *this

        Clock *p = (Clock*)(this); // 或者Clock *p = const_cast<Clock*>(this);
        p->second++; //ok
    }
};
}}}
<<Include(C++:this指针, "this指针", 2)>>

C++类与对象

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 void display(complex a);
   9 void set(complex *c, double r, double i);
  10 
  11 int main() {
  12     complex x, y;
  13     set(&x, 10.0, 20.0);
  14     set(&y, 1.0, -2.0);
  15     complex z = add(x, y);
  16     display(z);
  17 }

另一个例子:

   1 struct clock {
   2     int second;
   3     int minute;
   4     int hour;
   5 };
   6 void set_time( clock *c, int h, int m, int s) {
   7     c->second = s;
   8     c->minute = m;
   9     c->hour = h;
  10 }
  11 void show_time(const clock *c) {
  12     cout << c->hour << c->minute << c->second;
  13 }
  14 void inc_second(clock *c) {
  15     c->second ++;
  16     if(c->second == 60) {
  17         c->minute ++;
  18         c->second = 0;
  19         if(c->minute == 60) {
  20             c->hour ++;
  21             c->minute = 0;
  22             if(c->hour == 24)
  23                 c->hour = 0;
  24         }
  25     }
  26 }
  27 
  28 int main() {
  29     clock c;
  30     set_time(&c, 10, 10, 30);
  31     inc_second(&c);
  32     show_time(&c);
  33 }

再一个例子:

   1 #define MALE true
   2 #define FEMALE false
   3 struct student{
   4    char num[20];
   5    char name[10];
   6    bool gender;
   7 };
   8 void set(student &s, char id[], char n[], bool g){
   9     strcpy(s.num, id);
  10     strcpy(s.name, n);
  11     s.gender = g;
  12 }
  13 void display(student &s) {
  14     cout << s.num << s.name << (s.gender?"male":"female");
  15 } 
  16 int main() {
  17     student stud1, stud2;
  18     set(stud1, "110101", "Rose", FEMALE);
  19     set(stud2, "110102", "JACK", MALE);
  20     display(stud1);
  21     display(stud2);
  22 }

2. C++的类

C语言中,表示数据的结构体和操作这些结构体的函数是分开的。或者说数据结构和操作它的算法是分开的。这样数据和操作之间的关系不是很清晰。C++的做法是在C语言的基础上更进一步,将数据与操作这组数据的函数结合在一起,构成类(class)

   1 struct clock {
   2     int  hour, minute, second;
   3     void set_time(int h, int m, int s);
   4     void show_time();
   5     void inc_second();
   6 };

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

   1 struct clock {
   2     int  hour, minute, second;
   3     void set_time(int h, int m, int s) {
   4         hour = h;
   5         minute = m;
   6         second = s;
   7     }
   8     void show_time() {
   9         cout << hour << minute << second;
  10     }
  11     void inc_second(clock *c) {
  12         second ++;
  13         if(second == 60) {
  14             minute ++;
  15             second = 0;
  16             if(minute == 60) {
  17                 hour ++;
  18                 minute = 0;
  19                 if(hour == 24)
  20                     hour = 0;
  21             }
  22         }
  23     }
  24 };
  25 
  26 int main() {
  27     clock c1, c2;
  28     c1.set_time(10, 10, 30);
  29     c2.set_time(18, 00, 00);
  30     c1.show_time();
  31     c2.show_time();
  32 }

也可以分开定义,比如:

   1 //clock.h头文件:
   2 struct clock {
   3     int  hour, minute, second;
   4     void set_time(int h, int m, int s);
   5     void show_time();
   6     void inc_second();
   7 };
   8 
   9 //clock.cpp源文件:
  10 void clock::set_time(int h, int m, int s) {
  11     hour = h;
  12     minute = m;
  13     second = s;
  14 }
  15 void clock::show_time() {
  16     cout << hour << minute << second;
  17 }
  18 void clock::inc_second() {
  19     second ++;
  20     if(second == 60) {
  21         minute ++;
  22         second = 0;
  23         if(minute == 60) {
  24             hour ++;
  25             minute = 0;
  26             if(hour == 24)
  27                 hour = 0;
  28         }
  29     }
  30 }
  31 
  32 
  33 
  34 // main.cpp源文件
  35 int main() {
  36     clock c1, c2;
  37     c1.set_time(10, 10, 30);
  38     c2.set_time(18, 00, 00);
  39     c1.show_time();
  40     c2.show_time();
  41 }

另一个例子:

   1 const bool MALE = true;
   2 const bool FEMALE = false;
   3 struct student{
   4    string num;
   5    string name;
   6    bool gender;
   7    void set(string id, string n, bool g) {
   8        num = id;
   9        name = n;
  10        gender = g;
  11    }
  12    void diplay() {
  13        cout << num << name << (gender?"male":"female");
  14    }
  15 };
  16 int main() {
  17    student stud1, stud2;
  18    stud1.set("110101", "Rose", FEMALE);
  19    stud2.set("110102", "Jack", MALE);
  20    stud1.display();
  21    stud2.display();
  22 }

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

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

在堆空间分配和访问对象

   1 void f() {
   2     clock * my_clock = new clock; //分配对象
   3     clock * now = new clock;
   4     now->hour = 10;          //通过指针访问成员
   5     now->minute = 10;
   6     now->second = 30;  
   7     my_clock->set_time (9, 40, 0); //通过指针调用成员函数
   8     my_clock->show_time();
   9     delete my_clock;              //释放对象
  10     delete now;
  11 }

定义对象数组

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

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

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

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

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

成员函数也可以重载

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

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

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

3. 类的访问权限

在前面的clock类中,我们发现类中的数据成员hour、minute、second不需要被clock类的使用者直接访问。如果直接访问,还可能会带来副作用。在C++中增加了对类的成员的访问权限的控制,把成员分为public和private等。

   1 class clock {
   2 private: //只能在类内访问
   3     int hour, minute, second;  
   4 public:  //可以在类外访问
   5     void set_time(int h, int m, int s);
   6     void show_time();
   7 };

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

   1 int main(){
   2     clock d;
   3     d.set_time(8, 27, 30);  // 类内访问public, ok
   4     d.hour = 10;           // 类内访问private错误
   5 }
   6 void clock::set_time(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 clock_a {
   2     void set_time(int h, int m, int s); // public here
   3 private:
   4     int hour, minute, second; //private here
   5 };
   6 
   7 class clock_b {
   8     int hour, minute, second; // private here;
   9 public:
  10     void set_time(int h, int m, int s); // public here;
  11 };

另一个例子:

   1 class student{
   2 private:
   3     string num;
   4     string name;
   5     bool gender;
   6 public:
   7     void display() {
   8         cout << num << name << (gender?"male":"female");
   9     }
  10     void setnum(string n) {
  11         num = n;
  12     }
  13     void setname(char n) {
  14         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. 构造与析构

变量定以后,没有初始化就使用是错误的。比如:

   1 int main() {
   2     int i;  // 定义普通变量
   3     cout << i;  // 访问变量,错误,没有初始化
   4     clock time; // 定义对象,定义的同时并没有初始化
   5     time.show_time();          //错误,没有初始化
   6     time.set_time(20, 55, 10); //需要调用初始化函数后
   7     time.show_time();          //才能够访问
   8 }

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

   1 int main() {
   2     int i = 5;  // 定义普通变量,并初始化
   3     cout << i;  // 访问变量,ok
   4 }

而对于类变量,我们也希望可以完成类似的初始化,比如:

   1 int main() {
   2     clock c1(10,10, 30); // 定义对象,定义的同时并且初始化
   3     clock c2 = clock(10, 10, 31); // 或者这样初始化
   4     c1.show_time();
   5 }

要这样做可以在类中定义一个特殊的函数——构造函数。它在对象被定义时就被自动调用,以确保对象能够初始化。

   1 class clock{
   2     int hour, minute, second;
   3 public:
   4     /*构造函数,函数名与类名相同,没有返回类型*/
   5     clock(int h, int m, int s){
   6         hour = h;
   7         minute = m;
   8         second = s;
   9     }
  10     void show_time() {
  11         cout << hour << minute << second;
  12     }
  13 };

注意:构造函数是没有返回类型的,写成这样是语法错误:

   1 class clock{
   2 public:
   3     void clock (/*...*/); // error
   4 };

在定义了这样的构造函数以后,不使用参数来创建对象就变成了语法错误:

   1 int main() {
   2     clock c1; // 语法错误,没有这样的构造函数
   3     c1.show_time();
   4 }

有时希望对象不需要参数也可以初始化,初始化到一种默认的状态,则可以用没有参数的构造函数,称作默认构造函数(default constructor)

   1 class clock{
   2     int hour, minute, second;
   3 public:
   4     clock() {
   5         hour = minute = second = 0;
   6     }
   7     void show_time() {
   8         cout << hour << minute << second;
   9     }
  10 };
  11 
  12 int main() {
  13     clock c1; // 初始化为0时0分0秒
  14     c1.show_time();
  15     clock c2(15, 30, 0) // 错误,没有这样的构造函数
  16     clock c3(); // 定义函数而不是定义对象
  17 }

注意,要调用默认构造函数创建对象,不要在后面添加空的(),否则会被认为是函数声明,而不是对象定义。

如果要以多种不同的方式初始化对象,可以对构造函数进行重载。构造函数也可以有缺省参数:

   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 *s);
   7 };

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

   1 int main() {
   2     clock now;  //默认构造now对象call default constructor
   3     clock getup(6, 30, 30); 
   4     clock early(6);
   5     clock sleep("23:30:0");
   6     clock lesson(9, 21);
   7 }

一个所有参数具有默认参数的构造函数也是默认构造函数,比如

   1 class clock {
   2     int hour, minute, second;
   3 public:
   4     clock(int h =0, int m = 0, int s=0);
   5 };
   6 int main() {
   7     clock c1;
   8 }

构造函数和普通函数一样,也可以定义在类外:

   1 class clock{
   2     int hour, minute, second;
   3 public:
   4     clock(int h, int m, int s);
   5 };
   6 clock::clock( int h, int m, int s) {
   7     hour = h;
   8     minute = m;
   9     second = s;
  10 }
  11 
  12 int main() {
  13     clock sleep(22, 30, 0);
  14     clock getup(6, 30, 30); 
  15     clock now = clock(9, 21, 20);
  16     clock *pC = new clock(23, 30, 25); //构造函数会被自动调用
  17 }

在用new动态分配对象的时候,构造函数也会被自动调用。如果是用malloc分配内存,构造函数不会被自动调用。这是new和malloc最重要的区别。

   1 int main() {
   2     clock *pc = new clock(23, 30, 25); // 构造函数被掉用了
   3     clock *pd = (clock *)malloc(sizeof(clock)); //构造函数没有调用
   4 }

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

   1 class clock {
   2     int hour, minute, second;
   3 public:
   4     clock(int h =0, int m = 0, int s=0) {
   5         hour = h;
   6         minute = m;
   7         second = s;
   8     }
   9 };
  10 void f() {
  11     clock cls[10];
  12     clock *p = new clock[10];
  13 }

如果类的构造函数需要参数,则需要对数组进行初始化:

   1 class clock {
   2     int hour, minute, second;
   3 public:
   4     clock(int h , int m, int s) {
   5         hour = h;
   6         minute = m;
   7         second = s;
   8     }
   9 };
  10 void f() {
  11     clock cls[10] = {clock(0,0,0), clock(1,0,0), clock(2,0,0), clock(3,0,0), clock(4,0,0), clock(5,0,0), clock(6,0,0), clock(7,0,0), clock(8,0,0), clock(9,0,0), clock(10,0,0)};
  12     clock *p = new clock[10]; // 错误,没有办法初始化
  13 }

在前面写的构造函数中,我们对类的成员进行赋值,而不是初始化。在类的成员必须初始化而不能赋值的时候,这样做会带来问题。比如说类内有引用成员和const成员:

   1 class clock{
   2     const int hour, minute, second;
   3 public:
   4     clock(int h, int m, int s) {
   5          hour = h; // 错误,const变量不能够赋值
   6          minute = m; // 错误
   7          second = s; // 错误
   8     }
   9     void show_time() {
  10         cout << hour << minute << second;
  11     }
  12 };

要对成员进行初始化,需要使用构造函数的初始化列表:

   1 class clock{
   2     const int hour, minute, second;
   3 public:
   4     clock(int h, int m, int s) 
   5         : hour(h), minute(m), second(s) {
   6     }
   7     void show_time() {
   8         cout << hour << minute << second;
   9     }
  10 };

对于引用也要用同样的方式初始化。而对于普通变量也可以用初始化表进行初始化:

   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     void show_time() {
   8         cout << hour << minute << second;
   9     }
  10 };

在初始化列表里面要用()而不是=给出初始值。初始化列表还可以用在类中内嵌对象的情况:

   1 class world_clock {
   2     clock c;
   3     int timezone;
   4 public:
   5     world_clock(int hour, int minute, int second, int t)  
   6         :c(hour, minute, second), timezone(t) {
   7     }
   8 };

如果一个类中一个构造函数都没有写,编译器会自动生成一个默认构造函数。自动生成的构造函数可以调用内嵌类型的默认构造函数(如果有默认构造函数的话)。

   1 class world_clock {
   2 private:
   3     clock c;
   4     int timezone;
   5 public:
   6 };
   7 
   8 int main() {
   9     world_clock wc; // wc.timezone没有初始化,但是wc.c的构造函数会自动调用
  10 }

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

   1 class clock {
   2     int hour, minute, second;
   3 public:
   4     ~clock() {
   5         //作一些资源释放操作
   6     }
   7 };

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

   1 class clock {
   2     int hour, minute, second;
   3 public:
   4     ~clock() {
   5         cout << "destruct:"<< hour << minute << second;
   6     }
   7 };
   8 int main() {
   9     clock d1(4, 11, 1);
  10     if( true ){
  11         clock *d2 = new clock(9, 9, 9);
  12         clock d3(8, 10,0);
  13         //…
  14     } // d3 is destructed here
  15     delete d2; // d2 is destructed here
  16     clock d4;
  17 }  // d1,d4 is destructed here
  18 

析构函数一般用在需要资源释放的地方,比如:

   1 class mystring{
   2     char *s;
   3 public:
   4     mystring(char *str) {
   5         s = new char[strlen(str)+1];
   6         strcpy(s, str);
   7     }
   8     ~mystring() {
   9         delete[] s;
  10     }
  11 };
  12 int main() {
  13     mystring s("Hello");
  14 }

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 }

5. 对象的拷贝构造

对象的复制分为两种情况:赋值拷贝构造拷贝构造是指创建一个新对象,创建的同时让新对象的值与另一个已存在的对象一模一样。赋值是复制一个对象的值给另一个已存在的对象,使两个对象的值一样。比如

   1 void display( clock d ) {
   2    //...
   3 } 
   4 
   5 int main() {
   6     clock now;      //默认构造函数
   7     clock d = now;  //拷贝构造
   8     clock s( now ); //拷贝构造
   9     d = s; //赋值
  10     display(s);            //拷贝构造
  11 }

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

   1 class clock {
   2     int hour, minute, second;
   3 public:
   4     clock(int h, int m, int s) {
   5         hour = h;
   6         minute = m;
   7         second = s;
   8     }
   9     //这个拷贝构造函数实现的功能与编译器自动声成的相同,可以省略
  10     clock(clock& c) { 
  11          hour = c.hour; 
  12          minute = c.minute;
  13          second = c.second;
  14     }
  15 };
  16 
  17 int main() {
  18     clock c;
  19     clock d(c); //拷贝构造d,其值与c一样。
  20 }

何时需要自定义拷贝构造函数、析构函数?当涉及到内存管理时,一个类中有指针成员,指向动态分配的空间,那么通常需要写一组拷贝构造函数、析构函数和赋值操作符。例如:

   1 class mystring{
   2     char *s;
   3 public:
   4     mystring(char *str) {
   5         s = new char[strlen(str)+1];
   6         strcpy(s, str);
   7     }
   8     ~mystring() {
   9         delete[] s;
  10     }
  11     mystring(mystring &str) {
  12         s = new char[strlen(str.s)+1];
  13         strcpy(s, str.s);
  14     }
  15 };
  16 int main() {
  17     mystring s("Hello");
  18     mystring s2(s);
  19 }

再例如:写一个类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     arr.at(10) = 10; //error数组越界
   6 }

   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     double x, y;
   3 public:
   4     point(double x0, double y0) {
   5         x = x0;
   6         y = y0;
   7     }
   8 };
   9 
  10 class line {
  11     point start, end;
  12 public:
  13     line(double x0, double y0, double x1, double y1) {
  14         start.x = x0;
  15         start.y = y0;
  16         end.x = x1;
  17         end.y = y1;
  18     }
  19 };

这个程序遇到编译错误。一是因为point类的x,y是私有的,在line类的构造函数中访问point的x,y是错误的。二是构造line中内含的两个point对象start,end需要参数,而在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 };
  21 int main() {
  22     line l(1,2,3,4);
  23 }

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

如果内嵌的类有默认构造函数,则初始化列表可以省略。

   1 class point{
   2 private:
   3     double x, y;
   4 public:
   5     point(double x0 = 0, double y0 = 0) {
   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         cout << "line initializing" << endl;
  18     }
  19 };
  20 
  21 int main() {
  22     line l(1,2,3,4);
  23 }

初始化表还能够解决其它一些类型的成员的初始化问题,比如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. 静态成员

8. 静态成员

类的一般数据成员,每个对象都有这样一组数据成员,它们的空间是独立的。C++的类还有一种特殊的成员,它的空间是这个类的所有对象共享的,称为类静态数据成员

   1 class task {
   2 public:
   3     static unsigned n; //声明静态成员n
   4     int m;
   5 };
   6 unsigned task::n; //定义静态成员n
   7 int main() {
   8     task  t1, t2;
   9     t1.n = 10;
  10     cout << t2.n;
  11 }

这里n的空间在整个程序里面只有一个,t1,t2共享同一个n。

静态数据成员需要声明和定义。初始化不是在构造函数中进行,而是在定义的时候进行。访问静态数据成员可以和一般成员一样,通过对象名来访问,也可以不通过对象直接用类名来访问。

   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 = 1;// 初始化
   9 
  10 int main() {
  11     cout << task::n << endl;
  12     task s1;
  13     cout << s1.x << s1.n << endl;
  14     task s2;
  15     cout << s1.x << s1.n << endl;
  16     cout << s2.x << s2.n << endl;
  17     cout << task::n << endl;
  18 }

静态成员函数是只能访问静态成员(包括静态数据成员和静态成员函数)的成员函数。

   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 &instance() {
   4         static singleton s;
   5         return s; 
   6     }
   7 private:
   8     singleton() { /*...*/ }
   9     singleton(singleton&){ /*...*/ }
  10 };

统计所有学生的平均成绩:

   1 class student {
   2     string name;
   3     string num;
   4     float score;
   5     static float sum;
   6     static int count;
   7 public:
   8     student(string na, string nu, float sc) 
   9         : name(na), num(nu), score(sc) {
  10         count ++;
  11         sum += score;
  12     }
  13     static float average() {
  14         return sum / count;
  15     }
  16 };
  17 
  18 float student::sum = 0;
  19 int student::count = 0;
  20 
  21 int main() {
  22     cout << student::average();
  23     student a("Jack", "0511001", 59);
  24     cout << a.average() << Student::average();
  25     student b("Lisa", "0511002", 99);
  26     cout << a.average() << b.average() << Student::average();
  27 }


CategoryCpp

9. 友元

10. 友元

友元(friend)提供了让其它函数或类绕过访问权限的控制直接访问类的所有成员(包括私有的和保护的成员)的途径。友元函数是能够直接访问类的所有成员的函数。比如要写一个函数isequal比较两个点是否相同:

   1 class point {
   2 private:
   3     int x, y;
   4 public:
   5     friend bool isequal(point a, point b); //声明isequal函数是point类的友元函数
   6 };
   7 bool isequal(point a, point b) {
   8     return a.x == b.x && a.y == b.y; //友元函数可以直接访问类的私有成员x和y
   9 }
  10 
  11 int main() {
  12     point a, b;
  13     // initialize
  14     cout << isequal(a, b);
  15 }

实际上不用友员也可以实现同样的功能,只要将x,y变成公有或者提供公有的函数访问他们就可以了:

   1 class point {
   2 private:
   3     int x, y;
   4 public:
   5     int &get_x() { return x; }
   6     int &get_y() { return y; }
   7 };
   8 bool isequal(Point a, Point b) {
   9     return a.get_x() == b.get_x() && a.get_y() == b.get_y();
  10 }
  11 
  12 int main() {
  13     point a, b;
  14     // initialize
  15     cout << isequal(a, b);
  16 }

友元类:类A是类B的友员,则类A的所有成员函数都可以访问类B的所有成员(包括私有的和保护的)。

   1 class point {
   2     int x, y;
   3     friend class rectangle; // Rectangle是Point的友元类,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     bool in(point p) {
  12         return p.x >= p1.x && p.x < p2.x && p.y >= p1.y && p.y < p2.y;
  13     }
  14 };
  15 
  16 int main() {
  17     rectangle rect;
  18     // initialize
  19     rect.display();
  20 }

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


CategoryCpp

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 set_time(int h, int m, int s) {
   9         hour = h;
  10         minute = m;
  11         second = s;
  12     }
  13     void show_time() {
  14         cout << hour << minute << second;
  15     }
  16 };
  17 int main() {
  18     const int i = 10;
  19     cout << i*10;               // ok
  20     i++;                        // error!
  21     const clock  t(10, 30, 20);
  22     cout << t.second;           // ok
  23     t.second = 10;              // error
  24     t.set_time(10, 20, 10);      // error!
  25     t.show_time();               // error!
  26 }

const对象不能调用普通的成员函数,即使这个函数并不修改数据成员。对于不修改数据成员的成员函数,可以在声明后面加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 set_time(int h, int m, int s) {
   9         hour = h;
  10         minute = m;
  11         second = s;
  12     }
  13     void show_time() const {
  14         cout << hour << minute << second;
  15     }
  16 };
  17 int main() {
  18     const int i = 10;
  19     cout << i*10;               // ok
  20     i++;                        // error!
  21     const clock  t(10, 30, 20);
  22     cout << t.second;           // ok
  23     t.second = 10;              // error
  24     t.set_time(10, 20, 10);      // error!
  25     t.show_time();               // ok
  26 }

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

   1 int main() {
   2    const  clock  time(10, 20, 30);
   3    time.show_time();
   4    clock now(10, 10, 10);
   5    time.show_time();
   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 }

12. this指针

13. this指针

在类的成员函数(不包括静态成员函数)中,存在一个名为this的指针,该指针指向成员函数被调用时的对象。

   1 class clock {
   2     int second, minute, hour;
   3 public:
   4     clock(int h, int m, int s)
   5        : hour(h), minute(m), second(s)
   6     {
   7     }
   8     void show_time() {
   9         cout <<  second << minute << hour;
  10         //等价于 cout << this->second << this->minute << this->hour;
  11     }
  12 };
  13 int main() {
  14     clock  t1(10, 20, 30), t2(20, 30, 10);
  15     t1.show_time();
  16     t2.show_time();
  17 }

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

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

   1 class clock {
   2 public:
   3     void show_time() 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 };


CategoryCpp

The End

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

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