C++ Primer Plus 学习笔记(七)

第10章 对象和类

1. 抽象和类

类包含一种对象所具备或者说需要的数据类型,以及使用这些数据的方法。

类的定义与声明和结构体的使用类似,只不过结构的成员默认访问类型是 public。类使用关键字 class 声明定义类对象。将方法的实现细节与抽象分开就是封装。数据隐藏(将数据放在类的私有部分)是一种封装,将实现细节放在私有部分也是一种封装。

类的成员函数和数据成员都可以在私有或公有部分声明,OOP 的目标之一是数据隐藏,因此数据一般放在私有部分。类的私有部分只有类的方法可以访问。

可以在类声明中省略关键字 private,类对象默认访问控制:

class World
{
    float mass;  // 默认 private
public:
    void tella(void);
    ...
};   

创建类对象:World wd1;调用类对象的方法:wd1.tella();

定义类的成员函数时,需要包含声明类的文件,要使用作用域解析运算符 :: 来指出函数所属的类:

void World::tella(void)
{
    ...
}

定义位于类声明中的函数,会自动成为内联函数:

class Stock
{
private:
    long shares;
    double share_val;
    double total_val;
    void set_tot() { total_val = shares * share_val; }  // 内联函数
    ...
};

也可以将定义放在类声明之外,定义时加 inline 限定符,将它变为内联函数。由于内联函数的特性,它是内部链接的,所以需要在每个使用它的文件中,都定义一遍,一般就放在头文件了。

每个类的新对象都有自己的储存空间,用于储存其内部变量和类成员;但同一个类的所有对象都共享同一组类方法。

2. 类的构造函数和析构函数

每个类都应该要构造函数和析构函数。不能像结构那样直接对类对象初始化,是因为类对象的数据一般都是私有的,无法通过类对象直接访问这些数据,必须通过成员函数来访问。而在类对象出了它们的作用域后,释放这些数据,也就同样需要析构函数来完成。

构造函数没有返回值,函数名跟类的名称相同。定义格式如下:

Stock::Stock(const string & co, long n)
{
    ...
}

在使用构造函数时,一般有两种,显示和隐式调用。显式调用:Stock food = Stock("world Cabb", 252);     

隐式调用:Stock gar("Furry Mason", 50);

两种调用是等价的。调用构造函数初始化类对象也可以与 new 一起使用:

Stock *pstock = new Stock("Elec Game", 18);

对象无法像调用类方法一样调用构造函数,因为在构造函数调用之前是没有这个类对象的。

这种调用构造函数的方法有两种实现方式,由编译器决定哪种实现方式。第一种实现方式的行为与第二种调用方式相同,即直接创建对象gar并初始化。第二种实现方式是:先通过构造函数创建一个临时对象,然后将它复制到gar中,最后调用析构函数,释放临时对象。

默认构造函数是在构造函数未提供参数时,用来创建类对象的。若类中未声明任何构造函数,C++会自动提供默认构造函数,但这个构造函数不做任何工作。调用这个构造函数相当于给这个类对象分配了一块内存,而这个内存里有什么是未确定的。

而在类声明中创建了构造函数,则C++不会自动创建默认构造函数,当需要默认构造函数时,需要设计人员自己提供默认构造函数。

定义默认构造函数有两种方式,一种是默认函数的形式,另一种是无参数的形式:

Stock(const string & co = "Eorro:, long n = 0);  // 默认函数
Stock();  // 无参数形式

默认构造函数只能有一个,不要同时采用上述两种。

Stock first("Concrete Con", 15);  // 调用构造函数
Stock second();  // 声明一个返回值是Stock的函数
Stock third;  // 调用默认构造函数

析构函数的定义与声明:

~Stock();

Stock::~Stock()
{
    ...
}

与构造函数一样,析构函数也与类同名,析构函数没有参数,在构造函数使用 new 时,析构函数的定义更有意义。若析构函数实际不执行什么有意义的操作,可以不声明和定义它,编译器会自动生成一个什么都不做的隐式析构函数。析构函数的调用是编译器自己决定,不应在代码中显示的调用它(也有例外)。

类对象可以直接通过 = 赋值,默认情况下,将每个类的数据成员复制到目标对象中:gar = food;

构造函数不止可以初始化类对象,也可以赋值:

Stock stock1;  // 调用默认构造函数
stock1 = Stock("Nifty Foods", 10);  // 赋值

构造函数创建临时对象,复制完后,调用析构函数释放临时对象。

C++11 支持列表初始化:

Stock hot_tip = {"Der Plus", 10};
Stock jock {"Sport Age"};
Stock temp {};  // 调用默认构造函数

只要大括号中的参数与构造函数的参数列表匹配。

const 成员函数表示,该成员函数不会改变调用它的类对象数据,声明定义的方法为:

void show() const;  // 声明
void Stock::show()  // 定义
{
    ...
}

这种成员函数是来对应 const 的类对象的。 const 放在成员函数的后面这种形式,是在类成员函数中才这么用。

4. this 指针

this 指针指向用来调用成员函数的对象,它作为隐藏的参数传递给了方法。实际上,每个成员函数(包括构造和析构函数)都有一个 this 指针,指向调用对象。方法后面由 const 限定, 则该方法的 this 指针也是 const 限定的。

5. 对象数组

声明类对象数组与声明标准类型数组相同:Stock myarr[4];

这种方式调用默认构造函数创建每个数组成员,每个数组成员都可以调用类方法。可以用构造函数初始化数组:

Stock stocks[10] = {
    Stock("NanoSmart", 10),
    Stock()
};

剩下的数组成员使用默认构造函数初始化。初始化类对象数组元素的过程是,首先使用默认构造函数创建数组元素,然后在花括号中的构造函数创建临时对象,将临时对象的内容复制到相应的元素中。因此,创建类对象数组,这个类必须要有默认构造函数。

6. 类作用域

在类中定义的名称(如类数据成员和类成员函数名)的作用域都为整个类,作用域为整个类的名称只在该类内可知,在类外是不可知的。因此,不同类可以有同名的类成员。

如果要创建作用域是类的常量,不能在类错的私有或共有声明中,直接 const Months = 12;   

因为声明类只是描述了一种对象的形式,并不创建任何对象,即不分配内存。

由下面两种方法,可以创建类作用域常量。第一种是创建枚举:

class Bakery
{
private:
    enum {Month = 12};
    ...
}

这种方式的枚举,并没有创建类数据成员,Month 只是一个符号名称,在作用域是整个类的代码中遇到了,编译器会用12替代它。

第二种方法是使用关键字 static:

class Bekery
{
private:
    static const int Month = 12;
    ...
}

虽然创建了一个 Month 常量,但是它与其他静态变量储存在一起,并不储存在类对象中。因此只有一个 Month 常量被所有类对象共享。

C++11 提供了作用域内枚举,它在类中声明,可以这样避免同名冲突:

// 类声明中,可以用 struct 代替 class
enum class egg {Small, Medium, Large};
enum class t_shirt {Small, Medium, Large};

// 使用时
egg choice = egg::Large;
t_shirt Floyd = t_shirt::Large;

C++11 还提高了作用域内枚举的安全性,常规枚举可能在有些情况自动整型提升,而作用域枚举不允许隐式转换,但允许显示强制转换:

// 类声明中
enum egg_old {Small, Medium, Large};
enum class t_shirt {Small, Medium, large};

// 使用时
egg_old one = Medium;
t_shirt rolf = t_shirt::Large;
int king = one;  // one 由枚举类型隐式转换成 int
int ring = rolf;  // 非法
if (king < Large);  // 允许
if (king < t_shirt::Small);  // 非法
int Frodo = int (t_shirt::Large);  // 合法,Frodo设置为2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值