C++ Primer Plus学习笔记之对象和类

前言

个人觉得学习编程最有效的方法是阅读专业的书籍,通过阅读专业书籍可以构建更加系统化的知识体系。
一直以来都很想深入学习一下C++,将其作为自己的主力开发语言。现在为了完成自己这一直以来的心愿,准备认真学习《C++ Primer Plus》。
为了提高学习效率,在学习的过程中将通过发布学习笔记的方式,持续记录自己学习C++的过程。

一、过程性编程和面向对象编程

采用过程性编程方法时,首先考虑要遵循的步骤,然后考虑如何表示这些数据(并不需要程序一直运行,用户可能希望能够将数据存储在一个文件中,然后从这个文件中读取数据)。

采用OOP方法时,首先从用户的角度考虑对象——描述对象所需的数据以及描述用户与教据交互所需的操作。完成对接口的描述后,需要确定如何实现接口和数据存储。最后,使用新的设计方案创建出程序。

二、抽象和类

2、C++中的类

类是一种将抽象转换为用户定义类型的C++工具,它将数据表示和操纵数据的方法组合成一个整洁的包。

接下来定义类。一般来说,类规范由两个部分组成:

  • 类声明:以数据成员的方式描述数据部分,以成员函数(被称为方法)的方式插述公有接口
  • 类方法定义:描述如何实现类成员函数。
    简单地说,类声明提供了类的蓝图,而方法定义则提供了细节。

接口是一个共享框架,供两个系统(如在计算机和打印机之间或者用户或计算机程序之间)交互时使用。

要使用某个类,必须了解其公共接口;要编写类,必须创建其公共接口。

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

// stock00.h -- Stock class interface
// version 00
#ifndef STOCK00_H_
#define STOCK00_H_
#include <string>
class Stock // class declaration
{
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();
}; // note semicolon at the end
#endif

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

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

class World
{
    float mass;//默认private
    char name[20];//默认private
public:
    void tellall(void);
}

3、实现类成员函数

成员函救定义与常规函数定义非常相似,它们有函数头和函数体,也可以有返回类型和参数。但是它们还有两个特殊的特征:

  • 定义成员函数时,使用作用域解析运算符(::)来标识函数所属的类;
  • 类方法可以访同类的private组件。
// stock00.cpp -- implementing the Stock class
// version 00
#include <iostream>
#include "stock00.h"
void Stock::acquire(const std::string & co, long n, double pr)
{
    company = co;
    if (n < 0)
    {
        std::cout << "Number of shares can't be negative; "
                  << company << " shares set to 0.\n";
        shares = 0;
    }
    else
        shares = n;
    share_val = pr;
    set_tot();
}
void Stock::buy(long num, double price)
{
     if (num < 0)
    {
        std::cout << "Number of shares purchased can't be negative. "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares += num;
        share_val = price;
        set_tot();
    }
}
void Stock::sell(long num, double price)
{
    using std::cout;
    if (num < 0)
    {
        cout << "Number of shares sold can't be negative. "
             << "Transaction is aborted.\n";
    }
    else if (num > shares)
    {
        cout << "You can't sell more than you have! "
             << "Transaction is aborted.\n";
    }
    else
    {
        shares -= num;
        share_val = price;
        set_tot();
    }
}
void Stock::update(double price)
{
    share_val = price;
    set_tot();
}
void Stock::show()
{
    std::cout << "Company: " << company
              << " Shares: " << shares << '\n'
              << " Share Price: $" << share_val
              << " Total Worth: $" << total_val << '\n';
}

在OOP中,调用成员函数被称为发送消息,因此将同样的消息发送给两个不同的对象将调用同一个方法,但该方法被用于两个不同的对象。

要创建类对象,可以声明类变量,也可以使用new为类对象分配存储空间。可以将对象作为函数的参数和返回值,也可以将一个对象赋给另一个。

现在我们通过如下代码来使用类:

// usestok0.cpp -- the client program
// compile with stock.cpp
#include <iostream>
#include "stock00.h"
int main()
{
    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();
    fluffy_the_cat.sell(400, 20.00);
    fluffy_the_cat.show();
    fluffy_the_cat.buy(300000,40.125);
    fluffy_the_cat.show();
    fluffy_the_cat.sell(300000,0.125);
    fluffy_the_cat.show();
    // std::cin.get();
    return 0;
}

然后通过g++进行编译,编译语句:

g++ -fexec-charset=gbk -o E:\usertok0 E:\usertok0.cpp E:\stock00.cpp

程序运行结果如下:

Company: NanoSmart Shares: 20
  Share Price: $12.5 Total Worth: $250
Company: NanoSmart Shares: 35
  Share Price: $18.125 Total Worth: $634.375
You can't sell more than you have! Transaction is aborted.
Company: NanoSmart Shares: 35
  Share Price: $18.125 Total Worth: $634.375
Company: NanoSmart Shares: 300035
  Share Price: $40.125 Total Worth: $1.20389e+07
Company: NanoSmart Shares: 35
  Share Price: $0.125 Total Worth: $4.375

典型的类型声明格式如下:

class ClassName
{
private:
    data member declarations
public:
    member function prototypes
};

三、类的构造函数和析构函数

C++提供了一个特殊的成员函数——类构造函数,专门用于构造新对象、将值赋给它们的数据成员。构造函数的原型和函数名虽然没有返回值,但没有被声明为void类型。实际上,构造函数没有声明类型。

声明一个构造函数示例如下:

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

对构造函数进行定义:

Stock::Stock(const std::string & co, long n, double pr)
{
    company = co;
    if (n < 0)
    {
        std::cout << "Number of shares can't be negative; "
                  << company << " shares set to 0.\n";
        shares = 0;
    }
    else
        shares = n;
    share_val = pr;
    set_tot();
}

上述代码和本章前面的函数acquire()相同。区别在于,程序声明对象时,将自动调用构造函数。

接下来我们使用构造函数:

Stock food = Stock("World", 250 , 1.24);

这条语句与下面的等价:

Stock food("World", 250 , 1.24);

每次创建对象(甚至使用new动态分配内存)时,C++都使用类构造函数。下面是将构造函数与new一起使用的方法:

Stock stock = new Stock("Ele" , 18 , 19.0);

构造函数被用来创建对象,而不能通过对象来调用。

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

对于Stock类来说,默认构造函数可能如下:

Stock::Stock(){ }

默认构造函数没有参数,因为声明中不包含值。

当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数。因此,为类定义了构造函数后,就必须为它提供默认构造函数。
在设计类时,通常应提供对所有类成员做隐式初始化的默认构造函数。
隐式地调用默认构造函数时,不要使用圆括号。

用构造函数创建对象后,程序负责跟踪该对象,直到其过期为止。对象过期时,程序将自动调用析构函数。
析构函数需要在类名前加上~。析构函数没有参数,因此Stock析构函数的原型必须是这样的:

~Stock();

什么时候应调用析构函数由编译器决定,通常不应在代码中显式地调用析构函数。

  • 如界创建的是静态存储类对象,则其析构函数在程序结柬时自动被调用。
  • 如果创建的是自动存储类对象,则其析构函数将在程序执行完代码块时(该对象是在其中定义的)自动被调用。
  • 如果对象是通过new创建的,则它将驻留在栈内存或自由存储区中,当使用delete来释放内存时,其析构函数将自动被调用。
  • 最后,程序可以创建临时对象来完成特定的操作,在这种情况下,程序将在结束对该对象的使用时自动调用其析构函数。

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

C++引入了一种新的语法,保证函数不会修改调用对象,即将const关键字放在函数的括号后面,例如:

void Show() const;

四、this指针

this指针指向用来调用成员函数的对象(this被作为隐藏参数传递给方法)。一般来说,所有的类方法都将this指针设置为调用它的对象的地址。

五、对象数组

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

Stock mystuff[4];

六、类作用域

在类中定义的名称(如类数据成员名和类成员函数名)的作用域都为整个类,作用域为整个类的名称只在该类中是已知的,在类外是不可知的。因此,可以在不同类中使用相同的类成员名而不会引起冲突。另外,类作用域意味着不能从外部直接访问类的成员,公有成员函数也是如此。也就是说,要调用公有成员函数,必须通过对象。

C++提供了另一种在类中定义常量的方式——使用关键字static

class Stock
{
private:
    static const int M_Count = 100;
...
}

这将创建一个名为M_Count的常量,该常量将与其他静态变量存储在一起,而不是存储在对象中。

C++ 提供了一种新没,其枚举量的作用域为类:

enum class egg {Small, Big};

也可以使用关键字struct代替class。然后使用枚举名来限定枚举量:

egg choice = egg::Small;

七、抽象数据类型

类很适合用于描述ADT(abstract data type, ADT)。公有成员函数接口提供了ADT描述的服务,类的私有部分和类方法的代码提供了实现、这些实现对类的客户隐藏。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值