栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > C/C++/C#

第10章 对象和类-2

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

第10章 对象和类-2

待定
      • 10.3.4 析构函数
    • 10.4 this指针
    • 10.5 对象数组
    • 10.6 类作用域
      • 10.6.1 作用域为类的常量
    • 10.7 抽象数据类型

10.3.4 析构函数

构造函数创建对象后程序负责跟踪该对象直到过期为止;对象过期时,程序将自动调用一个特殊的成员函数——析构函数。
析构函数完成清理工作。比如,如果构造函数使用new分配内存,则析构函数将使用delete释放内存(析构函数中编写delete代码)。若构造函数没有使用new,则析构函数世界没有需要完成的任务,此时只需让编译器生成一个空的隐式析构函数即可(代码自动生成,可不写)。
析构函数的格式:类名~ 。同构造函数,析构函数可以没有返回值和声明类型,但区别于构造函数,析构函数没有参数。如Stock类对应的析构函数,它没有承担任何工作:

//声明:	 
~Stock();	

//定义:
Stock::~Stock();

如果创建的时静态存储类对象,析构函数将在程序结束时自动被调用;
如果创建的是自动存储类对象,析构函数将在程序执行完代码块时自动被调用;
如果是通过**new创建**,则它将在栈内存或自由存储区中,当使用delete来释放内存时自动调用析构函数;
程序可创建临时对象来完成特定操作,此时程序将在结束对该对象的使用时自动调用其析构函数。
补充:C++三种管理数据内存的方法:
 自动存储:函数内部定义的常规变量使用自动存储空间,称为自动变量(在所属函数被调用时自动产生结束时消亡),作用域在包含它的代码块(花括号中的一段代码),通常存储在栈中。
 静态存储:整个程序执行期间都存在的存储方式;有两种方法定义静态变量:函数外定义或使用关键字static。
 动态存储:new和delete运算符提供动态存储功能。动态存储内存在堆中。
如果代码没有编写析构函数,编译器将隐式地声明一个默认析构函数,进一步会提供默认析构函数的定义。
类对象存储在内存中是按栈的方式存储,先进后出,后进先出,故后创建的对象其对应的析构函数会先被调用。
提示:如果既可以通过初始化也可以通过赋值来设置对象的值,则应采用初始化方式效率更高。比如 int a = 10效率高于int a; a = 10。
5. C++11列表初始化(即大括号的初始化)
列表初始化也适用于类:

//声明
Stock::Stock(const std::string &co, long n = 0, double pr = 0.0);

//创建对象
Stock hot = {“Derivatives Plus”, 100, 20.6};
Stock jock{“Sport Age Storage, Inc”};
Stock temp {};

hot和jock与原始声明一一匹配,jock后面两个参数将为默认值;temp与默认构造函数匹配,因此将使用构造函数创建对象temp。
6. const成员函数
如下代码:

const Stock land = Stock(“Klughorn OP”);
land.show();     //这一行会报错

上述代码因为show()函数无法确保调用对象不被修改——调用对象和const一样,不应被修改(之前通过修改函数参数声明为const引用或指向const的指针来解决此问题)。但此处的show()没有任何参数。
对此,C++的解决方法是将const关键字放在函数的括号后面,即:

//声明
void show() const;

//定义
void Stock::show() const{ … };

这种类函数称为const成员函数。同尽可能将const引用和指针用作函数形参,只要类方法不修改调用对象就应将其声明为const
10.3.6 构造函数和析构函数小结
构造函数:一种特殊的类成员函数,在创建类对象的时候被调用;构造函数的名称和类名相同,可通过函数重载创建多个同名的构造函数(前提是特征标不同);无声明类型
若Bozo类的构造函数原型如下:

Bozo(const char *fname, cosnt char * lname);

初始化对象方法:

Bozo bozetta = Bozo(“A”, “B”);
Bozo fufu(“A”, “B”);
Bozo *pc = new Bozo(“A”, “B”);

若编译器支持C++11,则可使用列表初始化:

Bozo bozetta = {“A”, “B”};
Bozo fufu{“A”, “B”};
Bozo *pc = new Bozo{“A”, “B”};

警告:接受一个参数的构造函数允许使用赋值语法将对象初始化为一个值(不推荐)。
默认构造函数没有参数,因此如果创建对象时没有进行显示地初始化,则将调用默认构造函数;如果程序没有提供任何构造函数,编译器将为程序定义一个默认构造函数;默认构造函数可以没有任何参数,但如果有,则必须给所有参数都提供默认值。
析构函数:当对象被删除时,程序调用析构函数;每个类只能有一个析构函数;没有返回类型、没有参数;名称为“ ~ 类名 ”;如果构造函数使用了new,则必须提供delete的析构函数(在析构函数中编写delete相关代码)。

10.4 this指针

至上述章节,每个类成员函数都只设计一个对象,但有时候方法可能涉及到两个对象。比如比较两个Stock类中的价格更高者。
常规方法:定义一个成员函数topval(),函数调用stock1.topval()访问stock1对象的数据,stock2.topval()访问stock2对象的数据,如果需要进行比较,则必须将第二个对象作为参数传递给它,再返回一个引用,该应用指向股价较高对象,其方法对应原型:

const Stock & topval(const Stock &s) const;  //只是比较不进行修改,故均为const

//对应调用:
top = stock1.topvla(stock2);

上述函数隐式地访问一个对象,显示地访问另一个对象,并返回其中一个对象的引用。括号中的const表明该函数不会修改被显示访问的对象;括号后的const表明该函数不会修改被隐式访问的对象;返回的是两个const对象之一的引用,故返回类型也应为const。
topval()具体实现:

const Stock & topval(const Stock &s) const
{
if(s.total_val > total_val)
    return s;
else 
    return ???; 
}

上述代码中???要返回的是当前隐式访问的对象,但不知如何表示。
针对上述问题,C++提供被称为this的特殊指针来解决该问题。
this指针指向用来调用成员函数的对象(this被作为隐藏参数传递给方法)。函数调用stock1.topvla(stock2)将this设置为stock1对象的地址,使得这个指针可用于topval()方法(函数调用stock2.topvla(stock1)对应为stock2)。一般来说,所有的类方法都将this指针设置为调用它的对象的地址(topval()中的也是this->toptal_val的简写)。
注意:每个成员函数(包括构造函数和析构函数)都有一个this指针,this指针指向调用对象;如果方法要引用整个调用对象,则可使用表达式*this(取出this指针里边的内容——即类的对象);在函数括号后边使用const限定符将this限定为const,就不能使用this来修改对象的值(上述代码函数定义中括号后边的const)。
上述情况要返回的并不是this(this是对象的地址),而是对象本身,即*this(解除引用运算符*用于指针将得到指针指向的值),对应代码:

const Stock & topval(const Stock &s) const
{
if(s.total_val > total_val)   //实际上total_val == this->total_val
    return s;
else 
    return *this; 
}

返回类型为引用表示返回的是调用对象本身,而非其副本。
main()调用时部分代码:

Stock stock1(“A”, 12, 20.3);
stock1.show();

Stock stock2(“B”, 15, 98.9);
stock2.show();

Stock top = stock1.topval(stock2);
top.show;

this指针永远指向调用成员函数的对象,*this表示该对象,this为其地址

10.5 对象数组

用户要创建同一个类的多个对象时,可以创建独立独立对象变量(像前一节Stock类的做法),但创建对象数组更为合适。
声明对象数组的方法与声明标准类型数组相同:

调用隐式默认构造函数:

Stock mystuff[4];     //创建4个Stock类对象, 
mystuff[0].update();
mystuff[3].show();

调用构造函数(这种情况下必须为每个元素调用构造函数):

const int STKS = 2;
Stock mystuffs[STKS] = {Stock(“Nano”, 12.4, 39), Stock(“Fleep”, 30, 9.5)};

如果类包含多个构造函数,则可以对不同的元素使用不同的构造函数:

const int STKS = 10;
Stock mystuffs[STKS] = {Stock(“Nano”, 12.4, 39), Stock(), Stock(“Fleep”, 30, 9.5), };
//只初始化了数组中的前三个对象,剩余7个将使用默认构造函数
10.6 类作用域

在类中定义的名称(如类数据成员和类成员函数名)的作用域为整个类。因此,可以在不同类中使用相同的类成员名;不能从外部直接访问类的成员,公有成员函数亦如此(必须通过对象-stock.show())。同样,在定义成员函数时,必须使用作用域解析运算符(void Stock::update(double price))。
在类声明或成员函数定义中,可以使用未修饰的成员名称(未限定的名称,无类名+作用域解析运算符Stock::),如前面的set_tot()。在其它情况下,使用类成员名时,必须根据上文使用直接成员运算符.、间接成员运算符->或作用域解析运算符::。

10.6.1 作用域为类的常量

对于类中存在某个常量的情况,创建一个由所有对象共享的常量:

class Bakery
{
   private:
      const int Months = 12;         //fails
      double costs[Months];

上述代码const int Months = 12;不可行,因为声明类只是描述了对象的形式,并没有创建的对象(在创建对象之前没有用于存储值的空间)。
可通过两种方式解决上述问题:枚举和static
 枚举
在类中声明一个枚举。在类声明中声明的枚举的作用域为整个类,因此可以用枚举为整型常量提供作用域为整个类的符号名称,代码如下:

class Bakery
{
   private:
      enum {Months = 12};         //success
      double costs[Months];

Months并非变量而是一个符号名称,编译器遇到它时将用12替换它。这里的枚举只是为了创建符号常量,并不创建枚举类型的变量,因此不需要提供枚举名。
 static

class Bakery
{
   private:
      static const int Months = 12;         //success
      double costs[Months];

上述代码将创建一个名为Months的常量,该常量与其他静态变量存储在一起而非存储在对象中。即只有一个Months常量,被所有Bakery对象共享。

10.7 抽象数据类型

上述Stock类非常具体,实际通常通过定义类来表示更通用的概念。例如抽象数据类型(abstract data type, ADT),使用类就是一种非常好的方式。例如,栈存储数据,即总是从堆顶添加或删除数据。C++程序使用栈来管理自动变量,当新的自动变量被生成后,它们添加到堆顶;消亡时,从栈中删除它们(后入先出)。
首先,栈存储多个数据项(使得栈成为一个容器——一种更通用的抽象);其次,可对栈执行的操作:
 可创建空栈
 可将数据项添加到堆顶(压入)
 可从栈顶删除数据项(弹出)
 可查看栈是否填满
 可查看栈是否为空
可将上述描述转换为一个类声明,公有成员函数提供表示栈操作的接口,私有数据成员负责存储栈数据。因此,类概念很适合于ADT方法。

#ifdef __STACK_H__
#define __STACK_H__
typedef unsigned long Item;  //如果存放double把unsigned long换为double即可
class Stack
{
private:
       enum {MAX = 10};
       Item items[MAX];
public:
       Stack();
       boll isempty() const;
       bool isfull() const;
       bool push(const Item &item);
       bool pop(Item &item);
 
};

#endif

这个类不指定特定的类型来定义栈,而是根据通用的Item类型来描述,头文件使用typedef用Item代替unsigned long,如果需要double栈或结构类型的栈,只需修改typedef即可(类模板提供更为强大的办法将存储的数据类型和类设计隔离分开)。

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1037956.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号