c++基础知识(8)

c++ primer plus第十章知识,包括抽象和类、构造函数和析构函数、this指针、对象数组和类作用域。


抽象和类

c++中的类

类规范由两个部分组成

  • 类声明:以数据成员的方式描述数据部分,以成员函数(被称为方法)的方式描述公有接口
  • 类方法定义:描述如何实现类成员函数

通常,c++程序员将接口(类定义)放在头文件中,并将实现(类方法的代码)放在源代码文件中。

1
2
3
4
5
6
7
//类的定义
class className
{
private:

public:
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//定义Stock类
#ifndef __STOCK00_H_
#define __STOCK00_H_

#include<string>

class Stock
{
private:
std::string company;
long shares;
double share_val;
double total_val;
void set_tot(){total_val = shares * share_val;}//函数定义在类声明中,-->内联函数
public:
void acquire(const std::string &co, long n ,double pr);
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
void show();
};

#endif

关键字privatepublic描述了对类成员的访问控制。使用类对象的程序都可以直接访问公有部分,但只能通过公有成员函数(或友元函数)来访问对象的私有成员。因此,公有成员函数时程序和对象的私有成员之间的桥梁,提供了对象和程序之间的接口。防止程序直接访问数据被称为数据隐藏,c++中还提供了第三个访问控制关键字protected

通常将数据项放在私有部 分,组成类接口的成员函数放在公有部分

类对象的默认访问控制是private

类和结构的区别是,**结构体的默认访问类型是public**,而类为private

实现类成员函数

成员函数有函数头和函数体,也可以有返回类型和参数,除此之外还有两个特殊的特征:

  • 定义成员函数时,使用作用域解析运算符::来标识函数所属的类
  • 类方法可以访问类的private组件

例如,实现Stock类的update()成员函数

1
2
3
4
void Stock::update(double price)
{
...
}

使用类

1
2
3
4
5
6
Stock fluffy_the_cat;
fluffy_the_cat.acquire("NanoSmart", 20, 12.50);
fluffy_the_cat.show();

fluffy_the_cat.buy(15, 18.125);
fluffy_the_cat.show();

类的构造函数和析构函数

构造函数

每次创建类对象,c++都使用类构造函数

类的构造函数:专门用于构建新对象、将值赋给它们的数据成员。

构造函数和类的名称相同,并且没有返回值,没有被声明为void类型。

1
2
3
4
5
//将acquire修改为构造函数
Stock::Stock(const std::string &co, long n ,double pr)
{
...
}

使用构造函数

1
2
3
4
//显式调用构造函数
Stock food = Stock("World Cabbage", 250, 1.25);
//隐式调用
Stock garment("Furry Mason", 50, 2.5);

默认构造函数

默认构造函数是在未提供显示初始值时,用来创建对象的构造函数。

如果没有提供任何构造函数,则c++将自动提供默认构造函数。它是默认构造函数的隐式版本,不做任何工作

当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数。

如果程序员定义了构造函数,但是未定义默认构造函数,当想使用默认构造函数时,就会报错

1
Stock stock1;//这样声明会出错

如何定义默认构造函数?

  • 第一种方法:给已有的构造函数的所有参数提供默认值

    1
    Stock(const std::string &co = "Error", long n = 0, double pr = 0.0);
  • 第二种方法:通过函数重载来定义另外一个构造函数——一个没有参数的构造函数

析构函数

用构造函数创建对象后,程序负责跟踪该对象,直到其过期为止。对象过期时,程序将自动调用一个特殊的成员函数——析构函数,该函数将完成清理工作。

析构函数:在类名前加上~,例如,Stock类的析构函数为~Stock()。析构函数与构造函数一样可以没有返回值和声明类型,除此之外,析构函数没有参数。

什么时候应该调用析构函数?

由编译器决定,通常不应在代码中显式地调用析构函数。 如果创建的是静态存储类对象,则其析构函数将在程序结束时自动调用。如果创建的是自动存储类对象,则其析构函数将在程序执行完代码块时自动被调用。如果对象是通过new创建的,则它将驻留在栈内存或自动存储区中,当使用delete来释放内存时,其析构函数将自动被调用。

在类对象过期时析构函数自动被调用,因此必须有一个析构函数。如果程序员没有提供析构函数,编译器将隐式地声明一个默认析构函数,并在发现导致对象被删除的代码后,提供默认析构函数的定义。

this 指针

当类的成员函数涉及到两个对象时,需要使用c++的this指针

假设现有类Stock

1
2
3
4
5
6
7
8
class Stock
{
private:
...
double total_val;
public:
double total() const {return total_val;}
}

现有需求,找到两个Stock对象中total_val更大的那个对象,于是定义一个类成员函数

1
2
3
4
5
6
7
8
9
10
11
12
const Stock & topval(Stock & s) const;

const Stock & topval(Stock & s) const
{
if(s.total_val > total_val)
return s;
else
return ??? //此时应该返回*this
}

top = stock1.topval(stock2);//隐式访问stock1,显式访问stock2
top = stock2.topval(stock1);//隐式访问stock2,显式访问stock1

this指针指向用来调用成员函数的对象this是对象的地址,*this是对象本身。

函数调用stock1.topval(stock2)将this设置为stock1对象的地址;函数调用stock2.topval(stock1)将this设置为stock2对象的地址

对象数组

声明对象数组的方法和声明标准数据类型数组相同

1
Stcok mystuff[4];//调用默认构造函数

可以用构造函数来初始化数组元素。在这种情况下,必须为每一个元素调用构造函数

1
2
3
4
5
6
7
const int STKS = 4;
Stock stocks[STKS] = {
Stock("NanoSmart", 12.5, 20),
Stock("Boffo Objects", 200, 2.0),
Stock("Monolithic Obelidks", 130, 3.25),
Stock("Fleep Enterprises", 60, 6.5),
};

类作用域

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

类作用域意味着不能从外部直接访问类的成员,公有成员函数也是如此,即要调用公有成员函数,必须通过对象。

作用域为类的常量

有时,使符号常量的作用域为类很有用。例如类声明可能使字面值30来指定数组的长度。你可能会认为以下这样是可行的

1
2
3
4
5
6
class Bakery
{
private:
const int Months = 12;
double costs[Months];
}

这样子是行不通的,因为类声明只是描述对象的形式,并没有创建对象。因此在创建对象之前,将没有用于存储值的空间。

有两种方式可以实现这个目标,且效果相同

  • 在类中声明一个枚举

    1
    2
    3
    4
    5
    6
    class Bakery
    {
    private:
    enum {Months = 12};
    double costs[Months];
    }

    用这种方式声明枚举并不会创建类数据成员,也就是说所有对象中都不包含枚举。

  • 使用关键字static

    1
    2
    3
    4
    5
    6
    class Bakery
    {
    private:
    static const int Months = 12;
    double costs[Months];
    }

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!