C++ 类与对象

来源:互联网 发布:mysql geometry 编辑:程序博客网 时间:2024/06/11 04:33

C++之类与对象(1)

  下个阶段,我将讲解C++中面向对象的部分,也是C++对C语言改进的最重要的部分。以前C++也被叫做是"带类的C"。今天主要讲类的构成,成员函数以及对象的定义和使用。

  1.其实这一节,对于用C#开发的人来说,简直就是驾轻就熟啊。C++类的构成,我想从C的结构体开始说起。C中的结构体我想大家在熟悉不过了。

复制代码
 1 #include "stdafx.h"
2 #include <iostream>
3
4 usingnamespace std;
5
6 struct Kid
7 {
8 int age;
9 char*name;
10 char*sex;
11 };
12
13 int main()
14 {
15 Kid kid1;
16 kid1.age=10;
17 kid1.name="rookie_j";
18 kid1.sex="";
19 cout<<"姓名:"<<kid1.name<<endl<<"年龄:"<<kid1.age<<endl<<"性别:"<<kid1.sex<<endl;
20 return0;
21 }
复制代码

  但是C中的结构体存在缺点:1.main函数中的任意赋值语句都可以访问结构体中的成员,但在现实生活中并不是什么数据都可以被随意访问的,因此C语言中的结构体的数据是不安全的;2.结构体中的数据和对该数据的操作是分离的,并不是一个被封装起来的整体,因此使程序难以重用,影响了软件生产效率;于是C++中引入了类的概念。

  C++中类的一般格式为:

class Kid

{

  private:

    int age;    //私有成员

    char *name;

    char *sex;

  public:      //公有成员

    void setKid(int age,char *name,char *sex);

    void showKid();

};

C++中规定如果没有对类的成员加私有private,保护protected,或公有public,则默认为私有的。而对于C++的结构体来说,成员可以是私有的,保护的或公有的,但默认为公有的;还有要注意的是不能在类的声明中给数据成员赋值,比如:

class Kid

{

  private :

    int age=10;

    char *name="rookie_j";

    char *sex="男";

};

  一般情况下,一个类的数据成员应该声明为私有的,成员函数声明为共有的。这样,内部的数据隐藏在类中,在类的外部无法访问直接访问,使数据得到有效的保护。而公有的成员函数就成为一种与类外部沟通的接口。

  2.C++中的成员函数有两种,一种为普通的成员函数:

复制代码
 1 class Kid
2 {
3 private:
4 int age;
5 char*name;
6 char*sex;
7 public:
8 void setKid(int age,char*name,char*sex);
9 void showKid();
10
11 };
12
13
14 void Kid::setKid(int age,char*name,char*sex)
15 {
16 Kid::age=age;
17 Kid::name=name;
18 Kid::sex=sex;
19 }
20
21 void Kid::showKid()
22 {
23 cout<<"姓名:"<<name<<endl<<"年龄:"<<age<<endl<<"性别:"<<sex<<endl;
24 }
复制代码

要注意几点:1.类名和函数名之间应加上作用域运算符“::”,用于声明这个成员函数是属于哪一个类的,如果在函数名前没有类名,或既无类名又无作用域运算符“::”,比如:::showKid()或showKid(),那么这个函数不属于任何类,不是成员函数,而是普通函数;2.在类的声明中,成员函数原型的参数表中可以不说明参数的名字,而只说明它的类型,但在类外定义时必须既要说明参数类型又要说明参数名;

  另外一种就是内联成员函数,它又分显式声明和隐式声明:

隐式声明:

复制代码
 1 class Kid
2 {
3 private:
4 int age;
5 char*name;
6 char*sex;
7 public:
8 void setKid(int age,char*name,char*sex)
9 {
10 Kid::age=age;
11 Kid::name=name;
12 Kid::sex=sex;
13 }
14 void showKid()
15 {
16 cout<<"姓名:"<<name<<endl<<"年龄:"<<age<<endl<<"性别:"<<sex<<endl;
17 }
18
19 };
复制代码

 因为这种定义的内联成员函数没有使用关键字inline进行声明,因此叫隐式定义;

显式声明:

复制代码
 1 class Kid
2 {
3 private:
4 int age;
5 char*name;
6 char*sex;
7 public:
8 inline void setKid(int age,char*name,char*sex);
9 inline void showKid();
10 };
11
12
13 inline void Kid::setKid(int age,char*name,char*sex)
14 {
15 Kid::age=age;
16 Kid::name=name;
17 Kid::sex=sex;
18 }
19
20 inline void Kid::showKid()
21 {
22 cout<<"姓名:"<<name<<endl<<"年龄:"<<age<<endl<<"性别:"<<sex<<endl;
23 }
复制代码

内联函数的调用就是代码的扩展,而不是一般函数的调用的操作;但要注意的是使用inline定义的内联函数必须将类的声明和内联成员函数的定义都放在同一个文件中,否则编译时无法进行代码的置换

  3.在C++中,类与对象的关系,可以用数据类型int和整形变量i之间的关系来类比。int类型和类类型代表一种抽象的概念,而整形变量和类的对象代表具体的东西。C++把类的变量称为类的对象,对象也被称为类的实例;类的对象可以是:

 

复制代码
 1 class Kid
2 {
3 private:
4 int age;
5 char*name;
6 char*sex;
7 public:
8 inline void setKid(int age,char*name,char*sex);
9 inline void showKid();
10 }kid1,kid2;
复制代码

 

也可以是声明了类后,使用时在定义对象:Kid kid1,kid2;(声明一个类后,它并不接受和存储具体的值,只作为生成具体对象的一种“样板”,只有定义了对象后,系统才为对象分配存储空间,以存放对象中的成员);

  对对象中的成员的访问可以是:1.对象名.数据成员名/对象名.成员函数名(参数),比如kid1.setKid(10,"rookie_j","男");2.指针访问对象中成员,比如:class Kid{public: int age;}; Kid kid,*ptr; ptr=&kid;cout<<ptr->age//cout<<(*ptr).age;3.还记得上节所讲到的引用(reference)么,还可以通过引用来访问对象中的成员:class Kid{public: int age;}; Kid kid;Kid &ptr=kid; cout<<ptr.age//cout<<kid.age;

  4.最后我们还是一样,用一个实例来总结一下今天所讲的内容(开发工具:vs2010):

复制代码
 1 #include "stdafx.h"
2 #include <iostream>
3
4 usingnamespace std;
5
6 struct struct_Kid //结构体
7 {
8 int age; //默认公有
9 char*name;
10 char*sex;
11 }kid1;
12
13 class Kid
14 {
15 int age; //默认私有
16
17 private: //私有
18 char*name;
19 char*sex;
20
21 public: //公有
22 inline void setKid(int age,char*name,char*sex);//显示内联
23 /*{
24 Kid::age=age;
25 Kid::name=name;
26 Kid::sex=sex;
27 }*/
28 void showKid()//隐式内联
29 {
30 cout<<"类:"<<endl<<"姓名:"<<name<<endl<<"年龄:"<<age<<endl<<"性别:"<<sex<<endl;
31 }
32
33 }kid2;//直接定义对象
34
35
36 inline void Kid::setKid(int age,char*name,char*sex)
37 {
38 Kid::age=age;
39 Kid::name=name;
40 Kid::sex=sex;
41 }
42
43 int main()
44 {
45 //结构体
46 kid1.age=10;
47 kid1.name="rookie_j";
48 kid1.sex="";
49 cout<<"结构体:"<<endl<<"姓名:"<<kid1.name<<endl<<"年龄:"<<kid1.age<<endl<<"性别:"<<kid1.sex<<endl;
50
51 cout<<"--------------------"<<endl;
52
53 //
54 Kid kid3,*ptr;
55 Kid &kid4=kid3;
56 ptr=&kid2;
57
58 kid2.setKid(0,"rookie_y","");
59 kid2.showKid();
60
61 cout<<"--------------------"<<endl;
62
63 //指针调用成员函数
64 (*ptr).setKid(20,"rookie_y","");//或ptr->setKid(20,"rookie_z","女");
65 kid2.showKid();
66
67 cout<<"--------------------"<<endl;
68
69 //对象名调用成员函数
70 kid3.setKid(10,"rookie_x","");
71 kid3.showKid();
72
73 cout<<"--------------------"<<endl;
74
75 //引用调用成员函数
76 kid4.setKid(30,"rookie_x","");
77 kid3.showKid();
78
79 return0;
80 }
复制代码

结果:


 


C++之类与对象(2)

  接着上一节,今天讲C++中类的构造函数与析构函数,对象的赋值与复制.

  1.用过C#语言的人,都知道构造函数是一种特殊的成员函数,它主要用于对对象分配空间,进行初始化。构造函数的名字必须与类名相同。可以有任何类型的参数,但不返回任何值,是在建立对象时自动执行的。和上一节一样,还是用Kid类来说明。

复制代码
 1 class Kid
2 {
3 private:
4 int age;
5 char*name;
6 char*sex;
7 public:
8 Kid(int age,char*name,char*sex);
9 void showKid();
10 };
11
12 Kid::Kid(int age,char*name,char*sex)
13 {
14 Kid::age=age;
15 Kid::name=name;
16 Kid::sex=sex;
17 }
18
19 void Kid::showKid()
20 {
21 cout<<"姓名:"<<name<<endl<<"年龄:"<<age<<endl<<"性别:"<<sex<<endl;
22 }
复制代码

  接下来,建立对象并初始化:Kid kid(10,"rookie_j","男");另外一种使用new运算符动态建立对象:Kid *ptr=new Kid(10,"rookie_j","男");通过指针变量ptr来访问:ptr->showKid();当我们用new建立对象时,当不再使用它时,要用delete运算符释放它:delete ptr;和不同成员函数一样,如果构造函数定义在类体内部,则作为内联函数处理;

  在声明类时,对数据成员的初始化一般在构造函数中用赋值语句进行,但C++还提供了另外一种初始化数据成员的方法——用成员初始化表来实现对数据成员的初始化。它的一般形式为:类名::构造函数名([参数表]):[(成员初始化表)];成员初始化表的形式为:成员名1(初始值1),成员名2(初始值2),成员名2(初始值2);比如:

Kid::Kid(int age,char *name,char *sex):age(age),name(name),sex(sex){};

  接下来讲一下析构函数:在我第一次在C++里看到这个名词时,感觉这个知识点很深奥,结果看了以后,其实很简单。它的作用和构造函数刚好相反,用于撤销对象,如:释放分配给对象的内存空间。析构函数和构造函数名相同,但在其前面要加~符号,析构函数没有参数,也没有返回值,且不能重载,因此一个类中只有一个析构函数。以下三种情况,当对象的生命周期结束时,析构函数会被自动调用:(1)定义了全局对象,则在程序流程离开其作用域(如:main函数结束或调用Exit)时,调用该全局对象的析构函数;(2)对象被定义在函数体里,则当这个函数执行完后,该对象释放,析构函数被自动调用;(3)若一个对象使用new运算符动态创建的,在使用delete运算符释放它时,会自动调用析构函数;

View Code

结果:

  如果没有给类定义构造函数,则编译系统自动地生成一个默认的构造函数,比如在Kid类中编译系统会为其产生一个Kid::Kid(){};构造函数,这个默认的构造函数只能给对象开辟存储空间,不能给数据成员赋值,这时数据成员的初值就是随机数。对没有定义构造函数的类,其公有数据成员可以用初始化值表进行初始化,如:

复制代码
 1 class Kid
2 {
3 public:
4 int age;
5 char*name;
6 char*sex;
7 };
8
9 int main()
10 {
11
12 Kid kid={10,"Rookie_j",""};
13 cout<<"姓名:"<<kid.name<<endl<<"年龄:"<<kid.age<<endl<<"性别:"<<kid.sex<<endl;
14
15 return0;
16 }
复制代码

但只要一个类定义了构造函数,系统将不再给它提供默认构造函数;另外还有默认的析构函数(Kid::~Kid(){})一般来说默认的析构函数就能满足要求,但对一些需要做一些内部处理的则应该显式定义析构函数。带默认参数的构造函数和之前所说的带参数的成员函数是一样的,对于构造函数的重载,在这里我就不多说了,只想强调一点,如果是无参的构造函数创建对象,应该使用"类名 对象名"的形式,而不是"类名 对象名()";

  2.对象的赋值其实和变量的赋值差不多,也是用赋值运算符=进行的,只不过进行赋值的两个对象的类型必须相同,对象之间的赋值只是数据成员的赋值,而不对成员函数赋值;

 

View Code

 

结果:

  拷贝构造函数是一种特殊的构造函数,其形参是类对象的引用。它主要用于在建立一个新的对象时,使用已经存在的对象去初始化这个新对象。拷贝构造函数也是构造函数,所以函数名必须与类名相同,参数只有一个就是同类对象的引用,每个类必须要有一个拷贝构造函数。如果程序员自己不定义拷贝构造函数,系统会自动产生一个默认拷贝构造函数。调用拷贝构造函数的形式有代入法:类名 对象2(对象1)和赋值法:类名 对象2=对象1;

View Code

结果:

 

  同样的默认的拷贝构造函数:复制出与源对象的数据成员的值一样的新对象。调用拷贝构造函数的3种情况:(1)Kid kid2(kid1)或Kid kid2=kid1;(2)函数的形参是类的对象:fun(Kid kid){kid.showKid();}; int main(){Kid kid(10,"Rookie_j","男");fun(kid);return 0;};(3)函数返回值为类的对象:Kid fun(){Kid kid(10,"Rookie_j","男"); return kid;} int main(){ Kid kid; kid=fun();kid.showKid();return 0;};

  3.最后还是一样用一个实例来总结一下今天所说的内容(开发工具:vs2010):

View Code

结果:

但在运行的时候,发现一个问题如果把delete ptr; 这句的注释去掉,结果汽车的情况就显示不出来了;

结果:


C++之类与对象(3)

  在上篇的最后的实例程序代码中,我所写的成员函数中的参数变量名和数据成员名一样,为了编译时不发生错误,我在数据成员的前面加上"类名::"以区分。其实还有另外一种方法可以来加以区分,那就是C++中的自引用指针this。今天就讲一下C++中的this以及string类。可能代码不会很多,但突出原理;

  1.this这个关键字其实对于我来说并不陌生,在C#中就常用到。但今天我想说说为什么要有this,this起到什么作用?话还是要从类和对象说起,大家都知道定义一个对象,系统要给该对象分配存储空间,如果该类包含了数据成员和成员函数,就要分别给数据和函数的代码分配存储空间。按照正常额思路,如果类定义了2个对象,那么就应该分别给这2个对象的数据和函数分配空间。但事实并非如此,C++的编译系统只用了一段空间来存放在各个共同的函数代码段,在调用各个对象的成员函数时,都与调用这个公共的函数代码。因此,每个对象的存储空间都只是该对象数据成员所占用的存储空间,而不包括成员函数代码所占有的空间,函数代码是存储在对象空间之外的。但是问题就出来了,既然所有对象所调用的成员函数代码只是单独一封,那么成员函数是怎么知道当前调用自己的是哪一个对象呢?

  这时就诞生了this这个玩意,它是一个自引用的指针。每当创建一个对象时,系统就把this指针初始化为指向该对象,即this指针的值为当前调用成员函数的对象的起始地址。每当调用一个成员函数时,系统就把this指针作为一个隐含的参数传给该函数。不同的对象调用同一个成员函数时,C++编译器将根据成员函数的this指针所指向的对象来确定应该调用哪一个对象的数据成员。通过下面一个简单实例就可说明。

复制代码
 1 #include "stdafx.h"
2 #include <iostream>
3 #include <string>
4
5
6 class Num
7 {
8 private:
9 int a;
10 std::string objectName;
11 public:
12 Num(std::string objectName,int a);
13 void showNum();
14 };
15
16 Num::Num(std::string objectName,int a)
17 {
18 this->objectName=objectName;
19 this->a=a;
20 }
21
22 void Num::showNum()
23 {
24 std::cout<<this->objectName<<":this="<<this<<" a="<<this->a<<std::endl;
25 }
26
27
28 int main()
29 {
30 Num num1("num1",1);
31 Num num2("num2",2);
32 Num num3("num3",3);
33
34 num1.showNum();
35 num2.showNum();
36 num3.showNum();
37
38 return0;
39 }
复制代码

结果:

  2.在前几篇的代码中,我都是用Char *字符串指针来定义字符变量,还有就是在上一篇中的Kid类中的构造函数,用到过字符数组。并用字符复制函数strcpy,从中出现了问题,后来在博友的帮助下解决了。strcpy、strcat(字符串连接)和字符串长度(strlen),都是C中标准库函数的字符串操作。C++保留了这种格式,但是这种方法使用起来不太方便,另外数据与处理数据的函数相分离也不符合面向对象的思想。于是C++的标准库中,就出现了字符串类string。在使用string类型时,要在头文件中加入#include<string>;string的用法我在上述的例子中也有用到。在比如:string str("Hello C++")或string str="Hello C++";同时,也可以在表达式中把string对象和以'\0'结束符混在一起使用。

  3.最后来列个表说明一下string中常用的运算符:


 转载地址: http://www.cnblogs.com/CaiNiaoZJ/archive/2011/07/17/2108445.html



原创粉丝点击