C++笔记 --- 类与结构

 

目录

结构体

﹡类 -- 访问修饰符
﹡类 -- 成员函数
﹡类 -- 成员变量
﹡类 -- 操作符重载
﹡类 -- 友元类
﹡类 -- 抽象类
﹡类 -- 例程(匿名对象的调用,函数对象,单例模式)

 (本章节中例子都是用 VS2005 编译调试的)


 

结构体

声明形式:

struct 结构体名{

  成员声明;

  成员函数声明;

};

定义:

在c++允许声明结构体变量时省关键字struct(struct inflatable goose等价于inflatable goose)

成员访问修饰符:

  • private
  • protected
  • public

说明:

  • 结构体可以拥有成员函数,可以拥有析构函数和构造函数,但是默认访问权限是public的,用法和class相同
  • 在结构体定义完后要加‘;’
  • 引用结构体成员时,如果成员本身属于一个结构体,须一级一级找出最低级成员
  • 结构体类似 类 ,只是类的默认成员访问修饰符是 private 而结构体是 public,结构体也可以继承与另一个结构体,

引用方式:

  • 结构体变量名 . 成员名(或者成员函数名)
  • 结构体变量指针 -> 成员名(或者成员函数名)
  • (*结构体变量指针) . 成员名(或者成员函数名)
struct 结构体内存分配原理(class类似):
对应知识点:
1.内存对齐:
CPU 每次都是从以2字节(16位CPU)或是4字节(32位CPU)的整数倍的内存地址中读进数据的.所以编译器为了优化代码,往往会根据变量的大小,将其指定到合适的位置,即称为内存对齐.内存对齐主要是为让 CPU 在读取数据减少其操作次数的时候
2.结构体内存分配的规则:
1.结构体每个成员相对于结构体首地址的偏移量(offset)都是(这个)成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internaladding);
2.结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailingpadding);

例子:

简单声明和访问结构体变量成员:

View Code
 1 #include <iostream>
 2 #include <string>
 3 using namespace std;
 4 
 5 struct person
 6 {
 7     string name;
 8     bool sex;
 9     int age;
10     person(string n,bool s,int a)
11     {
12         name = n;
13         sex = s;
14         age = a;
15     }
16     person()
17     {
18         name = "";
19         sex = 0;
20         age = 0;
21     }
22     void showinfo()
23     {
24         cout<<"名字为: "<<name<<";  性别为: "<<(sex?"":"")<<";  年龄为: "<<age<<endl;
25     }
26 };
27 
28 void main()
29 {
30     person lisi("李四",0,18);
31     person *plisi = &lisi;
32     
33     //结构体变量访问成员的方法:
34     lisi.showinfo();
35     //指针的两种访问成员的方法;
36     plisi->showinfo();
37     (*plisi).showinfo();
38     
39     //修改结构体成员方法后的
40     lisi.age = 40;
41     lisi.showinfo();
42 
43     system("pause");
44 }
45 /***************************************
46 输出结果:
47 名字为: 李四;  性别为: 男;  年龄为: 18
48 名字为: 李四;  性别为: 男;  年龄为: 18
49 名字为: 李四;  性别为: 男;  年龄为: 18
50 名字为: 李四;  性别为: 男;  年龄为: 40
51 请按任意键继续. . .
52 ****************************************/

结构体成员为结构体的声明和访问其方法:

View Code
 1 struct CPU
 2 {
 3     string type;
 4     int price;
 5 };
 6 struct memory
 7 {
 8     string type;
 9     int price;
10 };
11 struct mainboard
12 {
13     string type;
14     int price;
15 };
16 struct computer
17 {
18     // 结构体中声明结构体变量
19 
20     CPU cpu;
21     mainboard mb;
22     memory my;
23 };
24 
25 void func()
26 {
27     computer cmpu;
28 
29     //对 cmpu 结构体中的 cpu, mb , my 结构体变量赋值
30     cmpu.cpu.price = 740;
31     cmpu.cpu.type = "Intel 酷睿i3";
32     cmpu.mb.price = 130;
33     cmpu.mb.type = "金士顿4GB";
34     cmpu.my.price = 500;
35     cmpu.my.type = "华硕P8H61";
36 }

结构体成员包含成员限制符:

View Code
 1 struct ex
 2 {
 3 private:
 4     string word;
 5 public:
 6     ex()
 7     {
 8         word = "hello!";
 9     }
10     void show()
11     {
12         cout<<word<<endl;
13     }
14 };
15 
16 void func()
17 {
18     ex ex1;
19     ex1.show();
20     ex1.word = "change the word"; //编译错误,因为 word 访问修饰符为 private
21     system("pause");
22 }

结构体内存对齐

View Code
#include<iostream> 
 
using namespace std; 
struct test1{ 
        char a; 
        double b; 
        char c; 
}; 
struct test2{ 
        char a; 
        int b; 
        char c; 
        char d; 
}; 
struct test3{ 
        char a; 
        char c; 
        char b; 
}; 
int main() 
{ 
        cout<<"the size of test1 is "<<sizeof(test1)<<endl; 
        cout<<"the size of test2 is "<<sizeof(test2)<<endl; 
        cout<<"the size of test3 is "<<sizeof(test3)<<endl; 
        return 0; 
} 

 

 [返回目录]


 

 

成员访问权限


分类:

  • private 私有变量和私有函数,其他的外部类,外部函数,派生类都无法访问这个部分的变量和函数,其只供给本类的成员函数调用和访问.
  • public 公有变量和公有函数,所有外部函数,派生类,外部类,本类都可以访问这个部分的变量和函数
  • protected 保护变量和保护函数,对于派生类来说此部分函数和变量是可以调用和访问的相当于是 public 权限,对于外部类和外部函数来说此部分是不能访问的相当于 private 权限

 

注意:

  • 对于外部函数来说,protected与private访问权限一样
  • 对于派生类来说,protected与public访问权限一样

例子:

View Code
 1 // 例1 -- public, private ,protected 对于外部的区别
 2 class A
 3 {
 4 private:
 5     int a;
 6 protected:
 7     int b;
 8 public:
 9     int c;
10 };
11 
12 void func()
13 {
14     A a;
15     a.a = 5; //错误, 因为外部函数无法访问类的 private 成员
16     a.b = 5; //错误,因为外部函数无法访问类的 protected 成员
17     //由此看来对于外部函数来说, private 和 protected 的访问修饰符的都是禁止外部函数访问
18     a.c = 5;
19     //而 public 访问修饰符则是允许外部访问
20 }
21 
22 
23 // 例2 -- public, private ,protected 对于子类的区别
24 class A
25 {
26 private:
27     int a;
28 public:
29     int b;
30 protected:
31     int c;
32 };
33 class B:public A
34 {
35     void func()
36     {
37         a = 5;//错误, 因为子类无法访问类的 private 成员
38         b = 5;// protected 访问修饰符允许子类部访问
39         c = 5;// public 访问修饰符允许子类访问
40         //由此看来对于子类来说, public 和 protected 的访问修饰符的都是允许子类访问
41     }
42 };

 

成员函数
[隐藏参数 this 指针][用户自定义成员函数(静态,非静态)][构造函数][析构函数][友元函数][虚函数][成员函数属性]


解释:

类的成员函数对于类产生的对象来说共用一组成员函数的,但各自有各自的成员变量

隐藏参数 this 指针:

C++ 编译器为类成员函数多准备了一个隐藏参数(在程序代码中看不到),举个例子,假设我们又一个 CMyClass 如下:

// CMyClass.h  ---------------------------
class CMyClass{
private:
    int nCount;
public:
    int getCount();
};

// CMyClass.cpp  -------------------------
int CMyClass::getCount()
{
         return nCount;
}

我们知道 C++ 中 CMyClass 类的所有实例共用成员函数 getCount 那么多个实例在调用 getCount 这个成员函数时候,getCount 就是通过这个隐藏的参数 this 找到正确的 nCount 并返回的(this 为指向实例对象的指针),那么 C++ 编译器针对 CMyClass::getCount 实际做出来的代码如下:

int CMyClass getCount((CMyClass*) this)
{
    return this->nCount
}

 那么调用也是一样的

void func()
{
    CMyClass exp;
    exp.getCount();
    //实际产生出来的效果是把exp的地址传递给 CMyClass::getCount 这个函数像这样: getCount(&exp)
}

当然我们有时候也不想让 this 这种作为该函数的最后一个参数,那么我们可以采取下面两个方法:

  • 不要使用类的成员函数
  • 使用 static 成员函数(静态成员函数),也就是在函数前面加上 static 关键字

用户自定义成员函数:

  • 静态函数成员:
    • 特点:
      • 静态成员函数只能访问静态成员变量
      • 在静态成员函数中是不能调用非静态函数和非静态成员数据变量
      • 静态成员函数属于类本身,在类中加载的时候,即为他们分配了空间
      • 静态成员和静态成员函数可以起到于全局变量和全局函数一样的作用,因此静态成员函数可以作为窗体编程的回调函数或多线程编程中线程的起始函数
    • 声明形式(用static声明的成员函数):
      • 访问修饰符 static 返回类型 函数名 (形参表);
    • 访问方式:
      • 类名::函数名(实参表)
      • 实例名.函数名(实参表);
      • 实例指针->函数名(实参表);
  • 非静态成员函数:
    • 特点:
      • 属于类的实例,也就是应该先产生对象实例才可以调用类的非静态成员函数
      • 非静态成员函数能访问静态成员变量与非静态成员变量
      • 非静态的成员函数可以调用静态成员变量和非静态成员变量和静态函数
    • 声明形式:
      • 访问修饰符 返回类型 函数名(形参表);
    • 访问方式:
      • 实例名.函数名(实参表);
      • 实例指针->函数名(实参表);
  • 例子:
    静态函数与非静态函数访问成员变量的区别:
    View Code
     1  class A
     2  {
     3  private:
     4      int a;
     5      static int b;
     6  public:
     7      void changeValue()
     8      {
     9          a = 5;
    10          b = 10;
    11      }
    12      static void changeValue2()
    13      {
    14          a = 5;   //错误,因为静态成员方法无法访问非静态成员变量
    15          b = 10;
    16      }
    17  };
    静态函数与非静态函数的调用区别:
    View Code
     1 class A
     2  {
     3  public:
     4      void show()
     5      {
     6          cout<<"this is not static function!"<<endl;
     7      }
     8      static void show2()
     9      {
    10          cout<<"this is static function!"<<endl;
    11      }
    12  };
    13  void main()
    14  {
    15      A a;
    16      A::show2();
    17      A::show();    //错误,因为 A:: 后只能跟静态成员方法
    18      a.show();
    19      a.show2();
    20      system("pause");
    21  }
    成员函数指针:
    View Code
    #include <iostream>
    using namespace std;
    
    class test 
    {
    public:
        static void sfunc(){cout<<"static function"<<endl;}
        void nfunc(){cout<<"member function"<<endl;}
        virtual void vfunc(){cout<<"virtual function"<<endl;}
    };
    
    int main()
    {
        test a;
        void (*ps)() = &test::sfunc;
        void (test::*pn)() = &test::nfunc;
        void (test::*pv)() = &test::vfunc;
    
        ps();
        (a.*pn)();
        (a.*pv)();
    
        system("pause");
        return 0;
    }
    /******************************************
    输出结果:
    static function
    member function
    virtual function
    请按任意键继续. . .
    ******************************************/

构造函数:

  • 由用户定义:    由系统调用完成对对象的初始化,且在其完成工作前对象是不存在的
  • 默认构造函数:    未提供显示初始化值时被用创建对象的构造函数
  • 调用方式:
    • 显示调用格式:    类名 对象名 = (形参列表);
    • 隐示调用格式:    类名 对象名 (形参列表);
    • 调用默认构造函数:  类名 对象名;
  • 注意:
    • 要创建对象而不显示地初始化则必须定义一个不接受任何参数的默认构造函数
    • 在构造函数中使用new来初始化指针(指向对象),则应在析构函数中使用delete
    • 如果有多个构造函数,则必须以相同的方式使用new,因为只有一个析构函数,不过可以在一个构造函数中使用new来初始化指令,而另一个构造函数中将该指令初始化为null或0,因为delete可作用与空指针
    • 用new开辟对象, 格式:
      •  类名 *指针名 = new 类名(形参表);
      •  类名 *指针名 = new 类名();   //调用默认构造函数
  • 例子:
    View Code
     1 // 例1 ---  调用构造函数
     2 class A
     3 {
     4 private:
     5     int a;
     6 public:
     7     A(int b):a(b){}
     8     //这种形式等价于 A(int b){ a = b;}
     9     A(){ a = 5;};
    10     void show(){cout<<"the value of a is: "<<a<<endl;}
    11 };
    12 void func()
    13 {
    14     A ex0;    // 调用默认构造函数 A();
    15     A ex1(7); // 调用 A(int b)(); 这个构造函数
    16     A ex2 = (8); // 调用 A(int b)(); 这个构造函数
    17 
    18     ex0.show();  
    19     ex1.show();
    20     ex2.show();
    21 }
    22 /******************************************
    23 输出结果:
    24 the value of a is: 5
    25 the value of a is: 7
    26 the value of a is: 8
    27 请按任意键继续. . .
    28 ******************************************/
    29 
    30 
    31 
    32 // 例2 ---  动态开辟对象实例调用构造函数
    33 class A
    34 {
    35 private:
    36     int a;
    37 public:
    38     A(int b):a(b){}
    39     //这种形式等价于 A(int b){ a = b;}
    40     A(){ a = 5;};
    41     void show(){cout<<"the value of a is: "<<a<<endl;}
    42 };
    43 void func()
    44 {
    45     A* ex0 = new A();    // 等价于 A* ex0 = new A;   调用默认构造函数 A();
    46     A* ex1 = new A(7); // 调用 A(int b)(); 这个构造函数
    47 
    48     ex0->show();  
    49     ex1->show();
    50 }
    51 /******************************************
    52 输出结果:
    53 the value of a is: 5
    54 the value of a is: 7
    55 请按任意键继续. . .
    56 ******************************************/

析构函数:

  • 定义:    由用户定义,却在对象生存周期即将结束时由系统自动调用
  • 格式:    ~ 类名();
  • 说明:    析构函数无返回值,无声明类型,无参数
  • 例子:
    View Code
    1 class A
    2 {
    3 private:
    4     int* a;
    5 public:
    6     A(){ a = new int(5);} //作用: 开辟一个整形变量
    7     ~A(){delete a;} //作用: 释放一个动态开辟的整形变量内存
    8 };

友元函数:

  • 说明:  在类型声明中将原型放入并在原型声明前加上关键字friend,但不要在定义时加上关键字friend,它的访问权限与类的成员函数的访问权限相同<声明而已为private,public,protect>
  • 作用:  把其他类中函数作为自己的友员成员函数,让特定的类成员成为另一个类的友元函数,而不必让整个类成为友元,但在使用时,必须小心排列各种声明和定义顺序(且需用到向前声明)
  • 向前声明格式:
    • class 类名 ;//向前声明本类
    • class 所用到的友员成员函数的类的类名 {....};//友元函数类的定义
    • class 类名 {…}//本类的定义
  • 注意:  友元函数不要在类的内部定义,不然编译不会通过
  • 例子:

友元函数(内部定义):

View Code
 1 class Point  {  
 2 public: 
 3     Point(double xx, double yy)
 4     {
 5         x=xx;
 6         y=yy;
 7     } 
 8     friend void Getxy(Point &a){  cout<<"position is: ("<<a.x<<","<<a.y<<")"<<endl;}
 9 private: 
10     double x, y;  
11 };

友元函数(外部定义):

View Code
 1 class Point  {  
 2 public: 
 3     Point(double xx, double yy)
 4     {
 5         x=xx;
 6         y=yy;
 7     } 
 8     friend void Getxy(Point &a);  
 9 private: 
10     double x, y;  
11 }; 
12 void Getxy(Point &a) 
13 {  
14     cout<<"position is: ("<<a.x<<","<<a.y<<")"<<endl;
15 } 

友元成员函数:

View Code
 1  class Date;                               //对 Date 类的向前声明
 2  class Time                                //定义 Time 类
 3  {
 4  public:
 5      Time(int,int,int);
 6      void display(Date&);                 //display 是 Time 类的成员函数,形参是 Date 类对象的引用
 7  private:
 8      int hour;
 9      int minute;
10      int sec;
11  };
12  class Date
13  {
14  public:
15      Date(int,int,int);
16      friend void Time::display(Date&);               //声明 Time 类的 display函数为本类的友元函数
17  private:
18      int month;
19      int day;
20      int year;
21  };
22  Time::Time(int h,int m,int s)                        //定义 Time 类的构造函数
23  {
24      hour = h;
25      minute = m;
26      sec = s;
27  }
28  void Time::display(Date&d)                 //display 函数的作用是输出年、月、日和时、分、秒
29  {
30      cout<<d.month<<"/"<<d.day<<"/"<<d.year<<endl;       //引用 Date 类对象中的私有数据
31      cout<<hour<<":"<<minute<<":"<<sec<<endl;             //引用本类对象中的私有数据
32  }
33  Date::Date(int m,int d,int y)                        //定义 Date 类的构造函数
34  {
35      month = m;
36      day = d;
37      year = y;
38  }
39 /*
40 请注意本程序的主函数中调用友元函数访问有关类的私有数据的方法:
41 1、在函数名 display 的前面要加 display 所在的对象名(t1);
42 2、display 成员函数的实参是 Date 类对象 d1,否则就不能访问 d1 中的私有数据;
43 3、在 Time::display 函数中引用 Date 类私有数据时必须加上对象名,如 d.month 。
44 */

虚函数:

  • 作用:  如果想通过指向派生类对象的基类指针,调用派生类中覆盖的成员函数,可以用虚函数的方法让派生类中覆盖的成员函数被调用
  • 格式:  virtual 返回类型 函数名 (形参表);
  • 注意事项:
    • 如果定义的类作为基类,则应将那些要在派生类中重新定义的类的方式声明为虚拟的
    • 如果使用指向对象的引用或指针调用虚方法,程序将使用做为对象类型的定义方法而不是使用做为了引用或指针定义的方法
  • 工作原理:[详细介绍]
    • 给每个对象添加一个隐藏成员,隐藏成员中保存了个指 向函数的地址数组的指针,这种数组称为虚函数表(vtbl)虚函数表中存储了为类对象进行声明的虚函数地址,派生类对象将包含一个指向独立地址的表指针,如果派生类提供了虚函数的新的定义,该虚函数地址被添加到vtbl中,注意,无论类中包含的虚函数是1还是10个都只需在对象中添加一个地址成员,只是表大小不同而已
  • 纯虚函数:
    • 格式:    virtual 函数类型 函数名(形参表)=0;<无函数体>
    • 作用:    有时,要在基类中不能给出虚函数实现代码,这是可将这种虚函数声明为纯虚函数
    • 注意:    有纯虚函数的类都是抽象类,并且抽象类是不能产生实例的.
  • 虚函数使用总结(摘抄于<<深入浅出MFC>>)
    • 如果你期望派生类从新定义一个成员函数,那么你应该在基类中把此函数设置为 virtual
    • 以单一指令调用不同函数,这种性质称为 Polymorphism(多态)
    • 虚拟函数是 C++ 语言的 Polymorphism 性质以动态绑定的关键
    • 既然抽象类中的虚函数不打算被调用,我们就不应该定义它,应该把它设置为纯虚函数(在函数声明后加 "=0" 即可)
    • 我们可以说,拥有纯虚函数者为抽象类(abstract class),以别于所谓的具体类(concrete class)
    • 抽象类不能产生实例,但我们可以拥有抽象类的指针,以便于操作抽象类的各个派生类
    • 虚函数派生下去的仍然为虚函数,而且可以省去 virtual 关键字
  • 例子:

虚函数:

View Code
 1 class A        //有虚函数的基类
 2 {
 3 public:
 4     virtual void show(){cout<<"这是基类的 A !"<<endl;}
 5     //用虚函数声明 show 函数
 6 };
 7 class B:public A
 8 {
 9 public:
10     void show(){cout<<"这是派生类 B !"<<endl;}
11 };
12 
13 class C        //没有虚函数的基类
14 {
15 public:
16     void show(){cout<<"这是基类 C !"<<endl;}
17 };
18 class D:public C
19 {
20 public:
21     void show(){cout<<"这是派生类 D !"<<endl;}
22 };
23 
24 void func( )
25 {
26     A* a = new B;   
27     C* c = new D;   
28     a->show(); //通过虚函数找到子类的成员函数 show
29     c->show(); //没有虚函数所以只能调用到基类的成员函数 show
30 }
31 /************************************
32 调用 func 函数后的输出结果:
33 这是派生类 B !
34 这是基类 C !
35 ************************************/

纯虚函数:

View Code
 1 class A        //有纯虚函数的基类
 2 {
 3 public:
 4     virtual void show() = NULL;
 5     //用纯虚函数声明 show 函数
 6 };
 7 //要注意的是如果类有纯虚函数那么这个类无法产生实例,并且纯虚函数的必须在其的派生类中实现
 8 class B:public A
 9 {
10 public:
11     void show(){cout<<"这是派生类 B !"<<endl;}//实现基类 A 的纯虚函数 show
12 };

利用虚函数/纯虚函数,打破子类访问权限.例子如下:

View Code
#include<iostream>
#include<cstdlib>

using namespace std;

class A{
public:
    virtual void output1() = 0;
    virtual void output2(){cout<<"this is A"<<endl;}
};

class B:public A{
private:
    void output1(){cout<<"this is success!"<<endl;}
protected:
    void output2(){cout<<"this is B"<<endl;}
};

void main()
{
    A* a;
    B b;
    a = &b;

    a->output1();
    a->output2(); //通过 a 我们访问到了 B 变量 b 的私有成员函数和保护成员函数 output1,output2
    //b.output1(); 
    //b.output2();//这里我们是无权限去访问类 B 变量 b 的私有成员函数和保护成员函数 output1,output2

    system("pause");
}

 

成员函数属性:

 函数       能否被继承   成员还是友员   默认能否生成   能否为虚函数   是否可以有返回值 
构造函数     ×        成员      √       ×        ×
析构函数     ×        成员      √       √        ×
=         ×        成员      √         √        √
&       √        任意      √       √        √
转换函数    √       成员      ×        √        ×
()       √       成员       ×         √        √
[ ]      √       成员       ×       √        √
→       √       成员       ×       √        √
op =       √       任意       ×       √        √
new      √       静态成员     ×       ×        void *
delete     √       静态成员     ×       ×        void
其他操作符   √       任意       ×         √       √
其他成员    √       成员       ×       √       √
友元      ×       友员       ×         ×       √ 

 

成员变量


静态成员:

  • 无论对象有多少,其共用一份静态成员,其具有全局性
  • 可以为private,public,protected,且需在外部声明并且没有this指针
  • 需要在 cpp 或 .h 中对其进行初始化(格式:    变量类型  类名::静态变量名 = 初始值; )
  • 静态成员变量属于类本身,在类中加载的时候,即为他们分配了空间,所以可以通过((类名.变量名)来访问.

非静态成员:

非静态成员变量属于类的实例,也就是应该先产生对象实例才会产生类的非静态成员变量

const成员:

只在调用构造函数时进行赋值,期间不能改动

枚举成员:

在类中声明一个枚举,在类声明中的这个枚举的作用域为整个类,因此可以用枚举为整形提供作用域为整个类的符号名称,用这种方式声明枚举并不会创建数据成员,也就是说,所以对象都不包含枚举成员

隐藏成员:

  • this指针:
    • 每个对象都有个this指针存放其地址,所以可以用*this引用对象,当成员函数的形参表中的参数与成员变量重名的时候,可以加以区分二者
  • 虚函数表指针:
    • 虚函数指针用于指向虚函数表,位于每个实例对象内存的头四字节,由于虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的.简称为V-Table.在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数.这样,在有虚函数的类的实例中分配了指向这个表的指针的内存,所以,当用父类的指针来操作一个子类的时候,这张虚函数表就显得尤为重要了,它就像一个地图一样,指明了实际所应该调用的函数

例子:

成员变量:

View Code
 1 class ex
 2 {
 3 private:
 4     enum {Len = 30};    //定义枚举成员
 5     static const int Len2 = 30;        //定义静态常量成员, 当然也效果等价于上面的枚举成员 Len
 6     char member[Len];        //定义非常量常成员
 7     const int constMember;    //定义常量成员
 8     static int staticMember;    //定义静态成员,若无显示的初始化,则其值为 0
 9 public:
10     ex(int j = 0,char *pmember = "null" )
11         :constMember(j)        //对常量成员 constMember 的初始化
12     {
13         for(int i=1;i<strlen(pmember);i++)
14             member[i-1] = pmember[i-1];
15     }
16 };
17 int ex::staticMember = 50;    //对静态成员 staticMember 的显示初始化

this 指针使用:

View Code
 1 class ex
 2 {
 3 private:
 4     int a;
 5 public:
 6     void changValue(int a)//此时函数参数 a 与成员变 a 重名
 7     {
 8         this->a = a;
 9         //this 代表但前实例的地址,由此可以用此地址调用成员变量即 this->a ,以此区分函数参数 a 与成员变 a
10     }
11 };

 

操作符重载


格式: 

返回类型 operater 操作符(成员列表)

限制:

  • 不能违反操作符原来的语法规则 <例:不能将求模操作% 重载成使用一个操作数>
  • 重载操作符不必是成员函数,但必须有一个操作数为用户定义类型
  • 不能定义新的操作符
  • 不能重载的操作符
    Ⅰ      sizeof 操作符;
    Ⅱ      . (成员操作符);
    Ⅳ.     *(成员指针操作符);
    Ⅳ      ::(作用解析操作符);
    Ⅴ      ?:(条件操作符);
    Ⅵ      typeid(一个RTTI操作符);
    Ⅶ 强制转换操作符(const_cast,dynamic,_cast,reinterpret_cast,static_cast)
  • 可被重载的操作符
    + , - , * , / , % , ^ , & , | , ~ , ! , = , > , < , += , -= , /= , %= , ^= , &= , |= , << , >> , >>= , <<= , == , != , <= , >= , && , || , ++ , -- , [] , () , -> , ->* , new , , , new[] , delete , delete[]
  • 只能成员函数重载的操作符:
    • = 赋值操作符
    • ()     函数调用操作符
    • []  下标操作符
    • &rar;∕→* 通过指针访问类成员操作符

i++,i--,++i,--i 的声明形式:

  • i++ 为:     返回类型 operator ++(int) {实现体}
  • ++i 为:     返回类型 operator ++()     {实现体}
  • i-- 与 --i 声明形式同上面相似

例子:

View Code
 1 class CMyString
 2 {
 3 private:
 4     char* m_str;
 5 public:
 6     CMyString(char *str)
 7     {
 8         int size;
 9         size = strlen(str) + 1;
10         m_str= new char[size];
11         strcpy(m_str,str);
12     }
13     CMyString()
14     {
15         m_str = NULL;
16     }
17     ~CMyString()
18     {
19         delete []m_str;
20     }
21     void ShowString()
22     {
23         cout << m_str << endl;
24     }
25     CMyString & operator=(CMyString& mystring)  //运算符重载函数
26     {
27         int size;
28         size = strlen(mystring.m_str) + 1;
29         m_str = new char[size];
30         strcpy(m_str,mystring.m_str);
31         return *this;
32     }
33 };
重载 = 操作符,并含有默认构造参数时候.
#include<iostream>
#include<cstdlib>

using namespace std;

class A{
public:
    A():a(0){};
    A(int argA=0):a(argA){}
    A operator =(A a){return *this;}
public:
    int a;
};

void main()
{
    A a(1);
    A b=a;
    cout<<b.a<<endl;
    //输出结果为1
    system("pause");
}

 

友元类


声明:

在类中用 friend class 友元类名;

作用:

在友元类的所有成员函数都可以访问此类中的所有数据

格式:

  • class 友元类名 ;//向前声明本类
  • class 拥有友元类的类名{....};//拥有友元类的类的定义
  • class 友元类名 {…}//友元的定义

特点:

  • 友元关系不能被继承
  • 友元关系是单向的,不具有交换性.若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明.
  • 友元关系不具有传递性.若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明

例子:

使用友元类:

View Code
 1 class B;   //友元类的向前声明
 2 class A    //拥有友元类的类的定义
 3 {
 4     int y;
 5 public:
 6     A()
 7     {
 8         y=4;
 9     }
10     friend B;
11 };
12 
13 class B  //友元类的定义
14 {
15 public:
16     void show(A &a)
17     {
18         cout<<a.y<<endl;
19     }
20 };

多个类包含一个友元类:

View Code
 1 class C;  //友元类的向前声明
 2 class A   //拥有友元类的类的定义
 3 {
 4     int x;
 5 public:
 6     A()
 7     {
 8         x=4;
 9     }
10     friend C;
11 };
12 
13 class B   //拥有友元类的类的定义
14 {
15     int y;
16 public:
17     B()
18     {
19         y=6;
20     }
21     friend C;
22 };
23 
24 class C    //友元类的定义
25 {
26 public:
27     void show(A &a,B &b)
28     {
29         cout<<a.x<<"  "<<b.y<<endl; 
30     }
31 };
32 
33 void func()
34 {
35     A a;
36     B b;
37     C c;
38     c.show(a,b);
39 }

一个类包含多个个友元类:

View Code
 1 class B;    //友元类向前声明
 2 class C;    //友元类向前声明
 3 class A     //拥有友元类的定义
 4 {
 5     int x;
 6 public:
 7     A()
 8     {
 9         x=4;
10     }
11     friend C;
12     friend B;
13 };
14 
15 class B   //友元类的定义
16 {
17 public:
18     void show(A &a)
19     {
20         cout<<"the value of a is: "<<a.x<<"        (in the class B)"<<endl; 
21     }
22 };
23 class C   //友元类的定义
24 {
25 public:
26     void show(A &a)
27     {
28         cout<<"the value of a is: "<<a.x<<"        (in the class C)"<<endl; 
29     }
30 };
31 
32 void func()
33 {
34     A a;
35     B b;
36     C c;
37     b.show(a);
38     c.show(a);
39 } 

 

抽象类


定义:

在类中声明了纯虚方法

注意:

  • 抽象类可以有零个或多个抽象方法,也可以包含非抽象方法
  • 抽象类可以没有抽象方法,但是有抽象方法的一定是抽象类
  • 抽象方法只涉及声明不负责实现
  • 抽象类可以派生出子类,子类的非抽象类必须抽象类的方法
  • 抽象类不能创建对象

例子:

1 class A
2 {
3 public:
4    virtual void show() const =0;
5 };

 

 [返回目录]

例程


匿名对象的调用

View Code
#include <iostream>
#include <cstdlib>
using namespace std;

class test{
private:
    int a;
    int b;
public:
    test(int argA=0,int argB=0):a(argA),b(argB){}
    void output(){cout<<"the a is "<<a<<" ,the b is "<<b<<endl;}
};
void testfun(test arg)
{
    arg.output();
}
void main()
{
    testfun(test(10,5));//创建匿名对象
    system("pause");
}

函数对象

View Code
#include <iostream>
#include <cstdlib>
using namespace std;
int add(int a,int b)
{
    return a+b;
}
int sub(int a,int b)
{
    return a-b;
}
class test{
public:
    int operator ()(int a,int b,int c){return add(sub(a,b),c);}
};

void main()
{
    test t;
    cout<<t(4,2,3)<<endl;
    system("pause");
}

单例模式

View Code
#include<iostream>
using namespace std;

class As
{
public:

    void show()
    {
        cout<<a<<endl;
    }
    static As* getinstance(int a)
    {
        As::a = a;
        return b;
    }
    static void deleteAs()
    {
        delete b;
    }
    virtual ~As()
    {

    }
private:
    static As *b;
    static int a;
    As()
    {
        a=1;
    }
};
As* As::b=new As();
int As::a = 0;
void main()
{
     //静态成员和成员函数的使用
    As* a = As::getinstance(5);
    a->show();
    delete a;
    if( a == NULL)
        cout<<"a isn't exist!"<<endl;
    else
        cout<<"a is exist!"<<endl;
    a->show();

    system("pause");
}

 

posted @ 2012-06-30 16:41  kzangv  阅读(3076)  评论(1编辑  收藏  举报
top