C++ primer plus 第10章对象和类
C++ primer plus 第10章对象和类
1 过程性编程和面向对象编程
2 抽象和类
2.1 ifndef
文件中的#ifndef
头件的中的#ifndef,这是一个很关键的东西。
需要注意的是,#ifndef起到的效果是防止一个源文件两次包含同一个头文件,而不是防止两个源文件包含同一个头文件。
而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。
还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:
#ifndef <标识>
#define <标识>
......
......
#endif
2.2 访问控制
公有成员函数是程序和对象的私有成员之间的桥梁,提供了对象和程序之间的接口。防止程序直接访问数据被称为数据隐藏。
类表示人们可以类方法的公有接口对类对象执行的操作,这是抽象。
类的数据成员可以是私有的(默认值),这意味着只能通过成员函数来访问这些数据,这是数据隐藏。
实现的具体细节,(如数据表示和方法的代码)都是隐藏的,这是封装。
2.3 类和结构
类对象的默认访问控制是private
,而结构的默认访问类型是public
。
class World
{
float mass;
char name[20]; //默认是private
public:
void tellall(void);
}
2.4 实现类成员函数
类成员函数有两个特征:
- 定义成员函数时,使用作用域解析运算符
::
来标识函数所属的类 - 类方法可以访问类的private组件
void Stock::update(double price)
3 类的构造函数和析构函数
每个类都分别定义了它的对象被初始化的方式,类通过一个或几个特殊的成员函数来控制其对象的初始化过程,这些函数叫构造函数。构造函数的任务是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。
3.1 声明和定义构造函数
构造函数原型(声明):
Stock(const & co, long n = 0, double pr = 0.0)
构造函数定义(没有返回类型):
Stock::Stock(const & co, long n = 0, double pr = 0.0)
{
company = co;
shares = n;
share_val = pr;
set_tot();
}
成员名和参数名不能相同!!!
3.2 使用构造函数
Stock food = Stock("World Cabbage", 50, 2.5);
或
Stock food("World Cabbage", 50, 2.5);
3.3 默认构造函数
定义默认构造函数有两种方法:
- 一种是给已有构造函数的所有参数提供默认值:
Stock(const & co, long n = 0, double pr = 0.0)
- 另一种是通过函数重载来定义另一个构造函数——一个没有参数的构造函数
Stock();
用户定义的默认构造函数通常给所有成员提供隐式初始值。例如,下面是Stock类定义的一个默认构造函数:
Stock::Stock()
{
company = "no name";
shares = 0;
share_val = 0.0;
set_tot();
}
提示: 在设计类时,通常应该提供对所有类成员做隐式初始化的默认构造函数。
声明对象变量时,初始化方式可以为:
Stock first("Concrete Conlomerate"); //调用构造函数
Stock second(); //声明一个返回Stock对象的函数
Stock third; //隐式的调用默认构造函数
Stock *fourth = new Stock("popo"); //动态对象
3.4 析构函数
类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
如果构造函数使用了new,则必须提供使用delete的析构函数,如:
class Student{
public:
Student(){m_name=new char[20];} //即在堆上定义----故在释放对象时必须要释放掉内存
private:
char *m_name;
};
故需要添加析构函数: //销毁时自动调用,没有则由系统默认生成
//没有返回值,也没有参数,从而不可重载
Student::~Student()
{
delete m_name;
m_name=NULL;
}
3.5 改进Stock类
4 this指针
当需要比较两个类中数据成员的大小,并返回变量值较大的类时,函数原型为:
const Stock & topval(const Stock & s) 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 ???????????; //函数无法返回调用该方法的对象
}
在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。每个成员函数(包括构造函数和析构函数)都有一个this指针。this指针指向调用对象。可以通过this->total_val(类成员)
来访问类成员。如果方法需要引用整个调用对象,则可以使用表达式*this
。在函数的括号后面使用const限定符将this限定为const,这样不能使用this指针来修改对象的值。
于是,可以使用this指针返回调用该方法的对象:
const Stock & Stock::topval(const Stock & s) const
{
if (s.total_val > total_val)
return s;
else
return *this;
}
5 对象数组
声明对象数组的方法和标准类型数组方法相同:
Stock mystuff[4]; //创建一个含有四个Stock对象的数组
使用构造函数来初始化数组元素时,必须为每一个元素调用构造函数。
const int STKS = 4;
Stock stocks[STKS] = {
Stock("Nono", 12.5, 20),
Stock("dsa", 200, 2.0),
Stock("Mono", 130, 20.25),
Stock("Fleep", 60, 6.5),
}
6 类作用域
当我们要创建一个由所有对象共享的常量是个不错的主意,我们可能认为下面做法是可行的:
class Bakery()
{
private:
const int Months = 12; //声明一个常量?这是错误的。
double costs[Months];
...
}
声明类只是描述了对象的形式,并没有创建对象,在创建对象之前,将没有用于存储值的空间。
此时有两种方法:
- 第一种为在类中声明一个枚举,在类声明中声明的枚举的作用域为整个类,因此可以用枚举为整型常量提供作用域为整个类的符号名称。
class Bakery()
{
private:
enum {Months = 12};
double costs[Months];
...
}
- 第二种方法为在类中定义常量的方式——使用关键字static
class Bakery()
{
private:
static const int Months = 12;
double costs[Months];
...
}
将常量与其他静态变量存储在一起,而不是存储在对象中。
7 抽象数据类型
ADT以通用的方式描述数据类型,而没有引入语言或实现细节。
下面简要介绍一下栈的特征,首先,栈存储了多个数据项(该特征使得栈成为了一个容器——一种更通用的抽象);其次栈由可对他执行的操作来描述。
- 可创建空栈
- 可将数据项添加到栈顶(压入)
- 可从栈顶删除数据项(弹出)
- 可查看栈是否填满
- 可查看栈是否为空
可以将上述描述转换为一个类声明,其中共有函数提供了表示栈操作的接口,而私有数据成员负责存储栈数据。类概念特别适合于ADT方法。
- 原文作者:jchen
- 原文链接:http://jchenTech.github.io/post/C++/C++-primer-plus-%E7%AC%AC10%E7%AB%A0%E5%AF%B9%E8%B1%A1%E5%92%8C%E7%B1%BB/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。