C++_primer_plus学习笔记 第10章 对象和类

本文详细介绍了C++中的对象和类,包括类的抽象、声明、实现、构造函数、析构函数、this指针的使用,以及对象数组和类作用域。讨论了数据隐藏、封装和类成员函数的实现,强调了面向对象编程的核心概念:抽象、封装、多态和继承。同时,还探讨了如何创建和使用类对象,以及如何通过构造函数和析构函数进行对象初始化和清理工作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本章内容包括:

  • 过程性编程和面向对象编程
  • 类概念
  • 如何定义和实现类
  • 公有类访问和私有类访问
  • 类的数据成员
  • 类方法(类函数成员)
  • 创建和使用类对象
  • 类的构造函数和析构函数
  • const成员函数
  • this指针
  • 创建对象数组
  • 类作用域
  • 抽象数据类型

面向对象编程(OOP)的重要特性:

  • 抽象
  • 封装和数据隐藏
  • 多态
  • 继承
  • 代码的可重用性

10.2 抽象和类

10.2.2 C++中的类

类将数据表示和操纵数据的方法组合成一个包

一般来说,类规范由两部分组成:

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

C++将接口(类定义)放在头文件,将实现(类方法)放在源代码

使用#ifndef等来多次包含同一个文件,遵循一种常见但不通用的约定——类名首字母大写

#pragma once
#ifndef __STOCK00_H__
#define __STOCK00_H__

#include <string>

class Stock    //关键字+类名
{
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(void);

private:    //私有
	std::string company;
	long shares;
	double share_val;
	double total_val;
	void set_tot(void) { total_val = shares * share_val; }    //内联
};

#endif // !__10.1_STOCK00_H__

1. 访问控制

  •  使用类对象的程序都可以直接访问公有部分,但只能通过公有成员函数或友元函数来访问对象的私有成员。
  • 公有成员函数是程序和对象私有成员之间的桥梁,提供了对象和程序之间的接口。
  • 防止程序之间访问数据被称为数据隐藏。
  • 将实现细节(private)放在一起并将它们与抽象(public)分开称为封装。
  • 将类函数定义和类声明放在不同文件是封装的另一个例子。

2. 控制对成员的访问:公有还是私有

  • 数据项通常放在私有部分,组成类接口的成员函数放在公有部分
  • 可以省略关键字private

10.2.3 实现类成员函数

#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)                //卖的股票数 < 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 yu 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(void)
{
	std::cout << "Company: " << company << "\tShares: " << shares
		<< "\tShare Price: $" << share_val << "\tTotal Worth: $" << total_val << std::endl;
}

成员函数有两个特殊的特征:

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

void Stock::update(double price)这种表示法意味着update()函数是Stock类的成员,还意味着可以将另一个类的成员函数也命名为update()。

作用域解析运算符确定了方法定义对应类的身份。标识符update()具有类作用域。Stock类的其他成员函数不必使用作用域解析运算符,就可以使用update()方法,因为它们属于同一个类,互相可见。

内联方法:定义位于类声明中的函数将自动成为内联函数。在类声明外定义的成员函数,只需在实现部分中定义函数时使用inline限定符,即可使其成为内联函数

10.2.4 使用类

#include <iostream>
#include "stock00.h"

int main(void)
{
	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();

	return 0;
}
  •  客户只能通过公有方式定义的接口使用服务器
  • 设计人员只能修改类设计的实现细节,而不能修改接口

10.2.6 小结

  • 类设计第一步:提供类声明。类声明可以包括数据成员和函数成员。私有部分的成员只能通过成员函数访问;公有部分的成员可以被类对象之间访问。通常,数据成员放私有,成员函数放公有。公有部分内容构成了设计的抽象部分——公有接口。数据封装到私有部分可以保护数据完整性——数据隐藏。
  • 类设计第二步:实现类成员函数。使用作用域解析运算符(::)指出成员函数属于哪个类。要创建对象(类的实例),只需将类名视为类型名即可,和int i一样;类成员函数通过类对象调用,和结构成员一样。

10.3 类的构造函数和析构函数

C++的目标之一是让使用类对象就像使用标准类型一样。

类构造函数:专门用于构造新对象,为数据成员赋值。

Stock类的构造函数是名为Stock()的成员函数。

构造函数没有返回值,但没有被声明为void类型。

10.3.2 使用构造函数

  • 显式调用构造函数:Stock food = Stock("AAA", 250, 1.25);
  • 隐式调用构造函数:Stock garment ("BBB", 50, 2.5);
  • 构造函数与new:Stock *pstock = new Stock ("CCC", 18, 19.0);
  • 无法使用对象来调用构造函数,因为在构造函数构造出对象前,对象是不存在的。
  • 因此构造函数被用来创建对象,而不能通过对象来调用。

10.3.3 默认构造函数

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

如果没有提供任何构造函数,C++将自动提供默认构造函数(隐式)。

定义默认构造函数的方式有两种:

  • 给已有构造函数的所有参数提供默认值
  • 通过函数重载定义另一个没有参数的构造函数。
  • 不要同时采用这两种方式

隐式调用构造函数时,不要使用圆括号。

Stock first("AAA");                //调用构造函数

Stock second();                    //返回值为Stock类的函数

Stock third;                           //调用默认构造函数

10.3.4 析构函数

析构函数:对象过期时程序自动调用析构函数完成清理工作。

例如,如果构造函数使用new来分配内存,析构函数将使用delete释放这些内存。

Stock类的析构函数为~Stock()。

和构造函数一样,析构函数也可以没有返回值和声明类型。

和构造函数不同的是,析构函数没有参数。

类必须要有一个析构函数。如果没有定义,则编译器隐式提供一个析构函数。

10.3.5 改进Stock类

//程序清单 10.4 stock10.h
#pragma once
#ifndef STOCK10_H_
#define STOCK10_H_

#include <string>

class Stock
{
public:
	Stock();    //默认构造函数
	~Stock();   //析构函数
	Stock(const std::string& co, long n = 0, double pr = 0);
    //构造函数,默认参数,从右向左
	void buy(long num, double price);
	void sell(long num, double price);
	void update(double price);
	void show(void) const;

private:
	std::string company;
	long shares;
	double share_val;
	double total_val;
	void set_tot(void) { total_val = shares * share_val; }
};

#endif // !STOCK10_H_
//程序清单 10.5
#include <iostream>
#include "stock10.h"

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();
}

Stock::Stock()
{
	std::cout << "Default constructor called\n";
	company = "no name";
	shares = 0;
	share_val = .0;
	total_val = 0.0;
}

Stock::~Stock()
{
	//std::cout << "Bye, " << company << std::endl;    //仅是标识析构调用时刻
}

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 yu 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(void) const
{
	std::cout << "Company: " << company << "\tShares: " << shares
		<< "\tShare Price: $" << share_val << "\tTotal Worth: $" << total_val << std::endl;
}

void show() const;以这种方式声明和定义的函数称为const成员函数。

只要类方法不修改调用对象,就应将其声明为const。

//程序清单 10.6
#include <iostream>
#include "stock10.h"

int main(void)
{
	{
		using std::cout;
		cout << "Using constructor to create new objects\n";
		Stock stock1("AAA", 12, 20.0);			//隐式构造函数
		stock1.show();
		Stock stock2 = Stock("BBB", 32, 23.0);	//显式构造函数
		stock2.show();

		cout << "Assigning stock1 to stock2:\n";
		stock2 = stock1;		//类对象支持赋值拷贝
		stock1.show();
		stock2.show();

		cout << "Using constructor to reset an object\n";
		stock1 = Stock("CCC", 10, 50.0);		//创建临时对象,重新赋值
		cout << "Revised stock1:\n";
		stock1.show();
		cout << "Done\n";
	}

	return 0;
}

10.3.6 构造函数和析构函数小结

  • 构造函数在创建类对象时被调用。
  • 构造函数的名称和类名相同,通过函数重载,可以创建多个同名的构造函数。
  • 构造函数用于初始化类对象成员
  • 默认构造函数可以没有任何参数,如果有,必须给所有参数都提供默认值。
  • 每个类只能由一个析构。

10.4 this指针

有时候方法涉及两个对象,这种情况需要使用C++的this指针

//程序清单 10.7 stock20.h
#pragma once
#ifndef STOCK20_H_
#define STOCK20_H_

#include <string>

class Stock
{
public:
	Stock();
	~Stock();
	Stock(const std::string& co, long n = 0, double pr = 0);
	void buy(long num, double price);
	void sell(long num, double price);
	void update(double price);
	void show(void) const;		//注意这个const
	const Stock& topval(const Stock& s) const;

private:
	std::string company;
	long shares;
	double share_val;
	double total_val;
	void set_tot(void) { total_val = shares * share_val; }
};

#endif // !STOCK20_H_
程序清单 10.8
#include <iostream>
#include "stock20.h"

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();
}

Stock::Stock()
{
	std::cout << "Default constructor called\n";
	company = "no name";
	shares = 0;
	share_val = .0;
	total_val = 0.0;
}

Stock::~Stock()
{
	//std::cout << "Bye, " << company << std::endl;
}

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 yu 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(void) const
{
	std::cout << "Company: " << company << "\tShares: " << shares
		<< "\tShare Price: $" << share_val << "\tTotal Worth: $" << total_val << std::endl;
}

const Stock& Stock::topval(const Stock& s) const		//注意这个const
{
	if (s.total_val > total_val)		//total_val == this->total_val
		return s;
	else
		return *this;
}

 const Stock & topval ( const Stock & s ) const;

该函数隐式地访问一个对象,而显式地访问另一个对象,并返回其中一个对象的引用。

括号中的const表明,该函数不会修改显式访问的对象;

括号后的const表明,该函数不会修改隐式访问的对象。

由于该函数返回两个const对象之一的引用,所以返回类型也为const引用,类型匹配。

top= stock1.topval(stock2);

top = stock2.topval(stock1);

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

 如果s.total_val大于total_val,则函数返回指向s的引用;否则,将返回调用该方法的对象。

this指针指向调用成员函数的对象,this设置为调用对象的地址。

一般来说,所有类方法都将this指针设置为调用它的对象的地址。

total_val == this->total_val

要返回的不是this,this是对象的地址,*this是对象本身。

10.5 对象数组

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

//程序清单 10.9
#include <iostream>
#include "stock20.h"

const int STKS = 4;

int main(void)
{
	using std::cout;
	using std::endl;

	Stock stocks[STKS] =
	{
		Stock("AAA", 12, 20.0),
		Stock("BBB", 200, 2.05),
		Stock("CCC", 130, 3.25),
		Stock("DDD", 60, 6.5)
	};

	cout << "Stock holding:\n";
	int st;
	for (st = 0; st < STKS; st++)
		stocks[st].show();
	cout << endl;

	const Stock* top = &stocks[0];    //类似const int * 不修改指向对象内容
	for (st = 1; st < STKS; st++)
		top = &top->topval(stocks[st]);
	cout << "Most valuable holding:\n";
	top->show();

	return 0;
}

10.6 类作用域

在类中定义的名称的作用域都为整个类。

10.6.1 作用域为类的常量

class Bakery

{

        private:

                const int Months = 12;        //FAILS

                double costs[Months];

                ...

}

 类声明只是描述对象的形式,并没有创建对象。

在创建对象前,没有用于存储值的空间。

第一种:枚举

enum{ Months = 12 };

第二种:static

static const int Months = 12;

10.7 抽象数据类型(abstract data type,ADT)

//程序清单 10.10 stack.h
//入栈出栈
#pragma once
#ifndef STACK_H_
#define STACK_H_

typedef unsigned long Item;    //类型别名

class Stack
{
public:
	Stack();
	~Stack();
	bool isempty(void) const;            //判空
	bool isfull(void) const;             //判满
	bool push(const Item& item);         //入栈
	bool pop(Item& item);                //出栈

private:
	enum { MAX = 10 };    //static const int MAX = 10;
	Item items[MAX];
	int top;              //栈顶指针

};
#endif // !STACK_H_
//程序清单 10.11
#include <iostream>
#include "stack.h"

Stack::Stack(void)    //默认构造函数    成员变量初始化
{
	top = 0;
}

Stack::~Stack(void)
{
}

bool Stack::isempty(void) const
{
	return top == 0;
}

bool Stack::isfull(void) const
{
	return top == MAX;
}

bool Stack::push(const Item& item)
{
	if (top < MAX)
	{
		items[top++] = item;
		return true;
	}
	else
		return false;
}

bool Stack::pop(Item& item)
{
	if (top > 0)
	{
		item = items[--top];
		return true;
	}
	else
		return false;
}
//程序清单 10.12
#include <iostream>
#include <cctype>
#include "stack.h"

int main(void)
{
	using namespace std;

	Stack st;
	unsigned long po;
	char ch;
	cout << "Enter a to add, p to pop, q to quit: ";
	while (cin >> ch && toupper(ch) != 'Q')
	{
		while (cin.get() != '\n')		//消耗回车
			continue;
		switch (ch)
		{
		case 'A':
		case 'a':
			cout << "Enter a po number to add: ";
			cin >> po;
			if (st.isfull())
				cout << "Stack already full.\n";
			else
				st.push(po);
			break;
		case 'p':
		case 'P':
			if (st.isempty())
				cout << "Stack already empty.\n";
			else
			{
				st.pop(po);
				cout << "PO #" << po << " popped.\n";
			}
			break;
		}
		cout << "Enter a to add, p to pop, q to quit: ";
	}
	cout << "Bye.\n";

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值