VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > C/C++语言编程 >
  • C++教程之类、实例和对象

 类、实例和对象
    类是从 C 语言中的结构体演变而来,结构体的成员变量也就演化成类的成员变 量,这时类只能存放数据。为了在类内部操纵这些数据,增加了成员函数的功能。 所谓成员函数就是在类的内部定义,用来操作类的成员变量的函数。随后对成员变 量和成员函数附上“私有”、“保护”和“公共”的访问权限,于是“类”便大致成型。事实 上,C++中结构体的功能大致和类相当,也有了成员函数。“成员”是成员变量和成 员函数的统称。 类的出现带动设计风格的巨大变化, 与传统设计方法相区别, 称之为“面向对象 设计”。关于面向对象设计、继承、封装、派生等概念性知识不在本教程范围之内, 书店里参考书象山一样,这儿只请大家对三个概念不要混淆:类、实例和对象。 “类”和结构体一样,是一种自定义的数据类型,但不是基本类型。“实例”是用 自己定义的“类”这个数据类型来定义的变量。这些一个一个的实例统称“对象”。另 外,“继承”和“派生”是同一件事的不同说法,B 类继承了 A 类,也就是说 A 类派生 了 B 类。
    class 类名 访问符:
    {
    成员变量定义; 成员函数定义;
    访问符:
    成员变量定义; 成员函数定义;
    访问符:
    成员变量定义; 成员函数定义; …… }
    //| //| //| //| //| //| //| //| //| //| //| //|
    class CSample public: int x1;
    CSample();
    {
    protected: int a; float sum(float f1, float f2); private: int m; double sum(double f1, double f2);
    …… }
    一般按习惯将 private:定义部分放在紧靠类名下面,并且 “private:”可以省略。“private:”下面定义的成员全是“私有”的,也就是只能在这个类 的成员函数里可以使用, 外部(包括派生的子类)不能使用。 “public:”下面定义的成员, 所有地方都能使用。 “protected:”下面定义的成员, 在派生的子类中则相当于“public”, 其它地方则相当于“private”。
    二、构造函数和析构函数
    “构造函数”是类产生实例时被调用,进行初始化设置,是一个特殊的成员函数, 函数名与类名相同,没有返回值。一般构造函数定义在“public:”下面,但有时为了 阻止多个实例的产生而特意定义在“private:”或“protected:”下面。当初始化时没有什 么需要设定时也可以不定义,编译时会自动生成一个默认的构造函数。构造函数的 重载使得实例的生成灵活而且方便, 默认的构造函数没有参数, 且是定义在“public:” 下面的。 “析构函数”是类的实例被销毁时调用,进行最后的处理,例如释放动态分配的 内存等。一般析构函数定义在“public:”下面,不需要时也可以不定义,编译时会自 动生成一个默认的析构函数。析构函数的函数名与类名相同,前面有“~”返回值。 下面的例子演示构造函数和析构函数被调用的顺序。
    #include  using namespace std; class CA { int a; public:
    CA(){ a = 0; cout << "构造函数: " << a << endl; } ~CA(){ cout << "析构函数: " << a << endl; }
    void
    }
    setA(int x) {
    a = x;
    void
    } };
    print() {
    cout << "print: " << a << endl;
    int
    { CA ca;
    main ( )
    //ca.a = 10;
    ca.setA(10); ca.print();
    //成员变量 a 是私有的,不能直接访问
    return
    }
    0;
    三、成员函数的定义和声明分开
    上面例子是将成员函数的定义和声明全写在类的定义体里面,更好的编程风格 是分开来写,也就是类定义体里面只写成员变量和成员函数的声明,而成员函数的 定义则写在类的定义体外。这样,类的定义体就可以移到“头文件”中去。在外部定 义时,成员函数名前面要加上“类名::”。
    //==test.h== class CA { void a; public:
    CA(); ~CA();
    void void
    };
    setA(int x); print();
    //==test.cpp== #include  #include "test.h"; using namespace std;
    CA::CA(){ CA:: a = 0; cout << "构造函数: " << a << endl; } CA::~CA(){ CA:: cout << "析构函数: " << a << endl; }
    void
    CA::setA(int x) { CA::
    a = x;
    }
    void
    }
    CA::print() { CA::
    cout << "print: " << a << endl;
    int
    {
    main ( )
    CA ca;
    //ca.a = 10;
    ca.setA(10); ca.print();
    //成员变量 a 是私有的,不能直接访问
    return
    }
    0;
    四、生成实例的 3 种方法
    了解生成实例的三种方法的细微区别是很重要的。①申明为变量,②从无名对 象复制,③申明为指针并动态生成。注意:指针的成员用“->”,而不用“.”。
    //==test.cpp== #include  #include " using namespace std; int
    { string strA("劝学网"); main ( )
    //直接调用构造函数生成实例
    cout << strA << strA.length() << endl;
    //先调用构造函数生成空字符串实例 strB = string("小雅"); //再调用构造函数生成无名实例,然后复制 给 strB 实例,无名实例立即销毁 cout << strB << strB.length() << endl; //这和上面①的方法的结果相 同
    string strB;
    //先定义一个指针,尚未分配空间 strC = new string("quan.cn"); //动态调用构造函数生成实例后,再 将实例地址赋给指针变量
    string *strC; cout << *strC << strC->length() << endl;
    delete return
    } 0;
    strC;
    //千万不要忘记释放
    06 章 成员函数和运算符的重载
    一、构造函数的重载
    构造函数可以重载,使得生成实例时非常方便。构造函数一般要对成员变量赋 初值,有 2 种写法:
    #include  #include  using namespace std; class
    stuff {
    string name;
    int age; public:
    stuff() {
    //这是写法一
    cout << name << "" << age << endl; "空 name = "空"; age = 0; cout << name << "" << age << endl; } stuff(string n, { cout << name << "" << age << endl; } string getName() {
    int
    a):name(n),age(a) :name(n),age(a)
    //这是写法二
    return
    }
    name;
    int
    } };
    getAge() {
    return
    age;
    int
    {
    main ( )
    stuff st2; stuff st1("小雅", 27);
    return
    }
    0;
    写法一是在构造函数体中赋值, 赋值前成员变量已经有了地址空间, 尚未有值。 写法二是一种特殊方法,是在成员变量分配空间的同时将参数的值赋给成员变量。 虽然写法二用的人少,但明显优于写法一。 事实上, 如果将成员变量的定义改为常量, “const string name;”和“const int age;”, 写法一将出错,而写法二仍然正确。
    二、运算符重载
    运算符重载对于普通函数和成员函数来说,格式稍有不同。
    //单目运算符
    成员函数: 普通函数:
    //双目运算符
    返回值类型 返回值类型
    operator operator
    运算符 运算符
    () ; (对象的类型) ;
    成员函数: 普通函数:
    //函数调用
    返回值类型 返回值类型
    operator operator
    运算符
    (对象的类型) ;
    运算符 (对象的类型 1, 对象的类型 2) ;
    成员函数:
    //数组元素
    返回值类型
    operator
    (任意的参数列) ;
    成员函数:
    //增 1/减 1 运算符
    返回值类型
    operator[]
    (参数类型) ;
    成员函数: 普通函数:
    返回值类型 返回值类型
    operator operator
    运算符 运算符
    (int) ; (对象的类型, int) ;
    #include  #include  using namespace std; class
    stuff {
    string name;
    int age; public:
    stuff(string n, {
    int
    a):name(n),age(a)
    cout << name << "" << age << endl; } string getName() {
    return
    }
    name;
    int
    }
    getAge() {
    return void void
    };
    age;
    operator +(int x); operator +(string s);
    //运算符重载的定义 //运算符重载的定义
    void
    {
    stuff::operator +(int x)
    //运算符重载的实装
    age = age + x; }
    void
    {
    stuff::operator +(string s)
    //运算符重载的实装
    name = name + s; }
    int
    {
    main ( )
    stuff st2("小雅", 27); st2 + 3; st2 + ".诗经";
    //+运算 //+运算
    cout << st2.getName() << st2.getAge() << endl;
    return
    }
    0;
    三、拷贝构造函数和赋值运算符
    本节内容较深,初学者请跳过。“拷贝构造函数”和“赋值运算符”都是将对象的 值复制一份然后传给另一对象。这二个功能也是类本身就具有的,但有很多场合原 封不动地复制给另外一个对象时反而会出错,例如在成员函数中有动态分配内存, 或者参数指针指向外部某一地址时,就有可能出错。
    要避免这些错误,我们可以重载“=”运算符以及拷贝构造函数, 将出错的因素排除。下例中为了演示,故意将赋值运算符重载函数 中不复制“姓名”,而拷贝构造函数中固定“年龄”。
    #include  #include  using namespace std; class
    stuff {
    string name;
    int age; public:
    stuff(string n, { cout << "构造函数 " << name << age << endl; } string getName() {
    int
    a):name(n),age(a)
    return
    }
    name;
    int
    }
    getAge() {
    return
    stuff&
    age;
    operator
    stuff(stuff& {
    //赋值运算符重载 x):name(x.name),age(20) //拷贝构造函数重载
    =(stuff& x); " << name << age << endl;
    cout << "拷贝构造函数 } };
    stuff& stuff::operator =(stuff& x) { age = x.age; cout << "赋值运算符 " << name << age << endl;
    return *this;
    }
    int
    {
    main ( )
    stuffst("小雅", 25); stuff st1("劝学网", 2);
    //调用通常的构造函数 //调用通常的构造函数
    st1 = st; stuff st2 = st;
    //因为不产生新的实例,所以调用的是赋值运算符 //因为产生新的实例,所以调用的是拷贝构造函数
    cout << st.getName() << st.getAge() << endl; cout << st1.getName() << st1.getAge() << endl; cout << st2.getName() << st2.getAge() << endl;
    return
    }
    0;
    四、类型转换
    当需要将当前类的实例直接赋值给其它类型的变量时自动转换类型,这其实还 是运算符重载。当需要其它类型直接赋值给当前类的实例时,只要增加构造函数就 行。
    #include  #include  using namespace std; class
    stuff {
    string name;
    int age; public:
    stuff(string n,
    int
    a):name(n),age(a) { }
    string getName() {
    return
    }
    name;
    int
    }
    getAge() {
    return
    age;
    operator int() return age;
    }
    {
    //stuff →
    int
    operator string() return name;
    } };
    {
    //stuff →
    string
    int
    {
    main ( )
    stuff st("小雅", 25); string m_name = st;
    int
    m_age = st;
    //stuff → string //stuff → int
    cout << m_name << endl; cout << m_age << endl;
    return
    }
    0;
相关教程