您的位置:首页 > 编程语言 > C语言/C++

C++快速入门 (十) 创建类

2013-02-05 13:38 281 查看

一,类的定义

(1). 什么是类

类是包含某种对象特性的集合,可以通过类来创建对象。如人(类) --> 具体的某个人(对象)。可以看些面向对象的文章。这里就不多阐述了。

(2). 创建类 和 类成员

使用关键字class 创建类。

class 类型 { <类的作用域> };

类可以包含,一般成员 和 函数成员,如下面的 Base

[align=left]class A[/align]
[align=left]{[/align]
[align=left] int a;[/align]
[align=left] void fun1()[/align]
[align=left] {[/align]
[align=left] cout << "A::fun1" << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]

(3). 访问修饰符

某些成员函数是提供给类本身访问的, 而不应该暴露给外界,比如某个类要有“说话”这个功能,而要达到“说话”的效果,声带,气管,肺等协调工作的细节外界不用知道,只要在“说话”这个成员函数内部实现就行。

被声明在 public 作用域的为公有成员,可以被 外界 访问,被声明在 private 作用域的为私有成员,只有类内部成员可以访问 , 如

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]private :[/align]
[align=left] int a; [/align]

[align=left]public :[/align]
[align=left] virtual void fun1()[/align]
[align=left] {[/align]
[align=left] cout << "Base::fun1" << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]

要点:

class 作用域默认 为 private
class 定义必须以 分号<;>结束

(4). 构造函数

类 的普遍用途是为 对象 服务的,既 我们让类中“生成”一个鲜活的可以被使用的对象。但要创建一个该类的对象就必须要初始化所有成员变量,并且这些成员的初始值也许在创建才能确定,于是C++就提供了一个创建对象时会第一个执行的“入口”函数,所有构造操作都可以在该函数内完成。这个函数也就顺理成章的被叫做
构造函数。要声明的构造函数必须与类同名且没有返回值部分

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]private :[/align]
[align=left] int a;[/align]

[align=left]public :[/align]
[align=left] Base(int x)[/align]
[align=left] {[/align]
[align=left] a = x ;[/align]
[align=left] }[/align]
[align=left]};[/align]

也可以在构造函数原型后直接设定对象成员的初始值

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]private :[/align]
[align=left] int a;[/align]
[align=left]public :[/align]
[align=left] Base( int x) : a( x ){ }[/align]
[align=left]};[/align]

需要注意的是,如果构造函数没有初始化类成员变量,而是交给系统自动初始化其值是不可预测的

(5). 构造函数参数的隐式类型转换

可以在构造函数前加修饰符 explicit,来避免构造函数参数进行隐式类型转换

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]private :[/align]
[align=left] int a;[/align]
[align=left]public :[/align]
[align=left] explicit Base( int x ) : a( x ){ }[/align]
[align=left]};[/align]

(6). 析构函数

当一个类的实例要被销毁前会调用该类的析构函数, 析构函数名和类名相同但其以"~"开始。

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]private :[/align]
[align=left] int a;[/align]
[align=left]public :[/align]
[align=left] Base( int x) : a( x ){ }[/align]
[align=left] ~Base(){}; // 析构函数[/align]
[align=left]};[/align]

析构函数没有参数没有返回值也不能被重载,并且一个类中只能有一个析构函数。

二,创建对象

(1). 创建一个对象

以下几种方式都可以创建一个类的实例

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] int a;[/align]
[align=left] explicit Base( int x = 0) : a( x ){ }[/align]
[align=left] void Say() { cout << "hello~~" << endl; }[/align]
[align=left]};[/align]

.......
[align=left]Base ba; // 调用无参构造函数[/align]
[align=left]Base ba2(11); // 调用无参构造函数[/align]
[align=left]Base ba3 = Base (10); // 调用有参构造函数[/align]

Base的构造函数使用了为参数指定默认值的技巧。当调用无参构造函数时,就可以匹配到该构造函数上(没有另外声明无参构造函数的情况下)。而声明 explicit 关键字可以防止构造函数的参数进行隐式类型转换

还需要说明的是:

使用 无参构造函数 创建对象时,后边不能加括号。
第三种创建方式汇编后会先产生一个临时对象, 所以效率没有前一种高
以上方式创建的对象会被分配在栈上,出其创建时的作用域时会被清除

(2). 使用 new / delete 操作符 创建对 / 销毁 对象

也可以使用 new 来创建一个对象,

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] int a;[/align]
[align=left] explicit Base( int x = 0) : a( x ){ }[/align]
[align=left] void Say() { cout << "hello~~" << endl; }[/align]
[align=left]};[/align]
.............
[align=left]Base *ba = new Base ;[/align]
[align=left]Base *ba2 = new Base ();[/align]
[align=left]delete ba;[/align]
[align=left]delete ba2;[/align]

可以看到使用 new 创建的对象需要用指针进行操作,使用 关键字new 创建的对象被存储在 堆(heap) 中,由于 堆 空间是由程序员自己管理的,所以当不在使用该对象时必须要使用关键字 delete 来手动释放 该对象所占的内存空间,
需要说明

new 和 delete 属于 C++ 操作符 (关键字)
操作符new 会为类的实例分配空间并调用其构造函数。同样 操作符delete 会调用其析构函数并且释放所分配的内存空间
new 和 delete 应该成对出现。
new 创建的对象在堆上,并且使用指针管理。
以上两种创建对象方式效果相同,但反汇编后可以看出 第二种效率比第一种低的多.

(3). “过时的”malloc / free

malloc / free 是 C 语言中 就存在的标准库函数,它们的作用是 申请 / 释放 一块指定大小的内存空间,其函数原型为:

[align=left]void* malloc(size_t size);[/align]
[align=left]void free(void * p);[/align]

malloc的使用示例

[align=left] int *pNum = NULL ;[/align]
[align=left] pNum = (int *)malloc( sizeof (int ) );[/align]
[align=left] if (pNum != NULL )[/align]
[align=left] {[/align]
[align=left] *pNum = 30;[/align]
[align=left] cout << (*pNum) << endl;[/align]
[align=left] }[/align]

[align=left] free(pNum);[/align]

大家都知道 C语言 是面向过程设计,而malloc 是 C 时的产物没有类等面向对象的语言机制,所以 malloc
只是申请指定大小内存,而不会做其他的事(如调用构造函数,析构函数等)。这样要使用 malloc创建类实例时就会很麻烦, 所以在 C++中尽量不要使用 malloc / free。

三,对象的默认函数

(1). 空类实例的默认函数

当为一个空类实例化时,会默认为添加四种函数

缺省无参构造函数
默认赋值操作符重载函数
默认拷贝函数
默认的析构函数

(2). 缺省无参构造函数

当类没有定义构造函数,实例化时会自动生成一个无参的构造函数,但如果该类哪怕已定义了一个任意签名的构造函数,实例化时都不会生成缺省无参构造函数。

(3). 默认的析构函数

不管是否已经自定义了析构函数,实例化时系统总是会生成一个析构函数。合成的析构函数负责释放类成员变量 并 调用自定义析构函数。需要注意的是,合成的析构函数不会释放指针成员所指向的对象

(4). 默认赋值操作符重载函数

和拷贝函数一样,编译器会生成默认的赋值操作符重载函数(以下简称赋值函数), 也可以自定义赋值函数来覆盖默认函数。考虑下面代码是否调用了赋值函数

Te t1(a, b );

Te t2 = t1;

答案是没有, 其中 Te t2 = t1 等同于 Te t2(t1); 而只有左值为已定义对象时才会调用赋值操作,如

Te t1(a, b );

Te t2(a, b );

t1 = t2;

某些时候,默认的赋值函数是无法正常工作的, 比如 当类成员为引用类型时,如

[align=left]class test {[/align]
[align=left]private :[/align]
[align=left] int &ref;[/align]
[align=left]public :[/align]
[align=left] explicit test( int & i ):ref( i ) {}[/align]
[align=left] void print() const {[/align]
[align=left] cout<<ref<<endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]void main()[/align]
[align=left]{[/align]
[align=left] int o=10,p=20;[/align]
[align=left] test ts1(o),ts2(p);[/align]
[align=left] ts2 = ts1; // 非法操作[/align]

[align=left]}[/align]

这是因为 默认赋值函数为浅拷贝, 默认赋值函数无法处理这种情况, 只有通过自定义赋值函数来解决,如

[align=left] test& operator=( const test & t )[/align]
[align=left] {[/align]
[align=left] ref = t .ref;[/align]
[align=left] return *this ;[/align]
[align=left] }[/align]

(5). 默认拷贝函数

默认的拷贝函数使用一个当前类型的另一个实例作为参数,对当前类实例进行初始化,默认拷贝函数只是对各参数进行了浅拷贝 ( 简单的赋值操作 ), 如

[align=left]class Te[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] ....[/align]
[align=left] string a;[/align]
[align=left] string & b;[/align]
[align=left] string * c;[/align]
[align=left] // 默认拷贝函数基本形式[/align]
[align=left] Te ( const Te & t )[/align]
[align=left] {[/align]
[align=left] a = t .a; // 值类型复制对象[/align]
[align=left] b = t .b; //引用类型只是复制了引用本身[/align]
[align=left] c = t .c; //指针类型也只是复制了指针本身[/align]
[align=left] }[/align]
[align=left]};[/align]

这种浅拷贝有人也叫 按位拷贝 或 位拷贝

(6). 自定义拷贝函数

可以自定义拷贝函数覆盖默认拷贝函数,

[align=left]class Te[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] int a;[/align]
[align=left] int *c;[/align]
[align=left] // 默认拷贝函数基本形式[/align]
[align=left] Te(){};[/align]
[align=left] Te ( const Te & t )[/align]
[align=left] {[/align]
[align=left] a = t .a;[/align]
[align=left] c = t .c;[/align]
[align=left] }[/align]
[align=left]};[/align]

可以看出 拷贝构造函数其实也是构造函数的一种,只是形参比较特殊。根据合成构造函数生成原则,该类实例化时将不会合成默认无参构造函数,所以这里我们要自定义一个无参构造函数。

(7). 法则

当一个类自定义了 赋值操作符函数,拷贝函数或析构函数 中的一个,大多数情况也需要自定义另外两个函数。比如 要实现一个类的深拷贝,只自定义拷贝函数是非常危险的。

-

<原创文章 转载请注明出处 http://blog.csdn.net/meiwm 谢谢>

作者:meiwm

出处:http://blog.csdn.net/meiwm
本文为原创,本文版权归作者所有。欢迎转载,但请务必保留此段声明,且在文章页面明显位置给出原文连接,谢谢合作。

-
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: