this指针
this指针指向用来调用成员函数的对象(this被作为隐藏参数传递给方法), 也就是this能指向本类对象, 看一个例子, 比较两个Stock对象的total_val值, 谁的total_val值大, 就返回哪个对象.
// 第一个const表示返回一个const的引用
// 第二个const表示, 要显示的访问一个const引用对象
// 第三个const表示, 函数不会修改隐式的访问对象的值
const Stock& Stock::topval(const Stock & s) const
{
// 这里的total_val实际是this->total_val的缩写
if(s.total_val > total_val)
{
return s;
}
// 由于this是指向本类对象的指针, 因此如果要返回本类对象可以用*this表示.
// 返回类型为引用, 意味着返回的是调用对象本身, 而不是其副本
return *this;
}
对象数组
声明对象数组的方法与声明标准类型数组相同:
Stock mystuf[4];
注意: 当程序创建未被显示初始化的类对象时, 总是调用默认构造函数, 也就是上述声明要求, Stock这个类要么没有显示的定义任何构造函数, 要么定义了一个显示默认构造函数(所有参数都有默认值).
如果类没有默认构造函数, 那么必须为每个元素调用构造函数:
const int STKS = 10;
Stock stocks[STKS] = {
Stock("No1", 20, 20.5),
Stock("No2", 30, 30.5),
// 可以对不同元素调用不同的构造函数
Stock()
};
上面这段代码只初始化了前三个元素, 对后七个元素使用的是默认的构造函数.
接下来看一个完整的例子:
// stock20.h
#ifndef STOCK20_H_
#define STOCK20_H_
#include <iostream>
class Stock
{
private:
std::string company;
int shares;
double share_val;
double total_val;
void set_tot() {total_val = shares * share_val;}
public:
// 这里有两个构造函数, 一个是默认构造函数, 一个是带参数的构造函数
Stock();
// 带有部分默认参数的构造函数
Stock(const std::string & co, long n = 0, double pr = 0.0);
// 析构函数
~Stock();
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
// 表示show不会修改隐式调用的成员变量
void show() const;
const Stock & topval(const Stock & s) const;
};
#endif
第二个文件:
// 第二个文件
// stock20.cpp
// Stock类函数的实现, 及构造函数的实现
#include <iostream>
#include "stock20.h"
// 默认构造函数
Stock::Stock()
{
company = "default name";
shares = 0;
share_val = 0;
total_val = 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" << std::endl;
shares = 0;
} else {
shares = n;
}
share_val = pr;
set_tot();
}
Stock::~Stock()
{
std::cout << "Bye, " << company << "\n";
}
void Stock::buy(long num, double price)
{
if(num < 0)
{
std::cout << "Number of shares purchased can't be negative Transaction is aborted " << std::endl;
} 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 " << std::endl;
} else if(num > shares){
cout << "You can't sell more than you have, Transaction is aborted" << std::endl;
} else {
shares -= num;
share_val = price;
set_tot();
}
}
void Stock::update(double price)
{
share_val = price;
set_tot();
}
void Stock::show() const
{
using std::cout;
using std::ios_base;
// 将cout的输出格式化为#.###
ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield);
std::streamsize prec = cout.precision(3);
std::cout << "Company: " << company << " Shares: " << shares << std::endl;
// 格式化为#.##
cout.precision(2);
std::cout << " Share Price: $" << share_val << "Total Worth: $" << total_val << std::endl;
// 设置会原来的格式
cout.setf(orig, ios_base::floatfield);
cout.precision(prec);
std::cout << " 2 Share Price: $" << share_val << "Total Worth: $" << total_val << std::endl;
}
// 使用this指针
const Stock & Stock::topval(const Stock & s) const
{
// 这里的total_val实际是this->total_val的缩写
if(s.total_val > total_val)
{
return s;
} else {
// this指向本类对象, 所以*this就是本类对象
// 返回值是const Stock & 因此返回的是本类的引用而不是副本
return *this;
}
}
第三个文件:
// usestock2.cpp
// compile with stock20.cpp
#include <iostream>
#include "stock20.h"
const int STKS = 4;
int main()
{
Stock stocks[STKS] = {
Stock("No1", 20, 20.5),
Stock("No2", 30, 30.5),
Stock("No3", 50, 50.5),
// 可以对不同元素调用不同的构造函数
Stock("No4", 10, 10.5)
};
std::cout << "Stock holdings: \n";
int st;
for(st = 0; st < STKS; st++) {
stocks[st].show();
}
const Stock * top = &stocks[0];
for(st = 1; st < STKS; st++){
top = &(top->topval(stocks[st]));
}
std::cout << "\n Most valuable holding : \n";
top->show();
return 0;
}
程序运行结果为:
类作用域
如何在类中声明一个符号常量, 使得其作用域为类, 例如:类声明可能使用字面值30来指定数组的长度, 由于该常量对于所有对象来说都是相同的.
class Bakery
{
private:
const int Months = 12;
double costs[Months];
...
}
这种写法是不可行的, 因为声明类知识描述了对象的形式, 并没有创建对象, 因此在创建对象前, 将没有用于存储值的空间, 但是仍有两种方式来实现这个目标:
1.在类中声明一个枚举. 在类声明中声明的枚举的作用域为整个类, 因此可以用枚举为整形常量提供作用域为整个类的符号名称. 例如:
class Bakery
{
private:
// 由于这里不打算创建枚举类型的变量, 因此不需要提供枚举名.
enum {Months = 12};
double consts[Months];
...
}
注意:这种方式声明枚举并不会创建类数据成员. 也就是说, 所有对象中都不包含枚举. 另外, Months只是一个符号名称, 在作用域为整个类的代码中遇到它时, 编译器将用30来替换它.
2.使用关键字static:
class Bakery
{
private:
static const int Months = 12;
double costs[Months];
...
}
这将创建一个名为Months的常量, 该常量将与其他静态变量储存在一起, 而不是储存在对象中. 因此只有一个Months常量, 被所有Bakery对象共享.
作用域内枚举
传统的枚举存在一些问题, 其中之一就是两个枚举定义中的枚举量可能发生冲突, 例如:
enum egg {Samll, Medium, Large, Jumbo};
enum t_shirt {Samll, Medium, Large, Jumbo};
这无法通过编译, 因为egg Samll和t_shirt Small位于相同的作用域内, 他们将发生冲突. 未解决这种问题, C++11提供了一种新枚举, 其枚举量的作用域为类, 写法如下:
enum class egg{Samll, Medium, Large, Jumbo};
enum class t_shirt {Samll, Medium, Large, Jumbo};
也可以使用struct代替class. 需要使用枚举名来限定枚举量:
egg choice = egg::Large;
t_shirt Floyd = t_shirt::Large;
C++11还提供了作用域内枚举的类型安全. 在有些情况下, 常规枚举将自动转换为整型, 如将其赋给int变量或用于比较表达式时, 但作用域内枚举不能隐式的转换为整型.
// 常规枚举类型
enum egg_old {Samll, Medium, Large, Jumbo};
// 作用域内枚举
enum class t_shirt {Samll, Medium, Large, Jumbo};
// 常规枚举变量
egg_old one = Medium;
// 作用域内枚举变量
t_shirt rolf = t_shirt::Large;
// 隐式转换为int, 合法的
int king = one;
// 非法的, 作用域内枚举不允许隐式转换
int ring = rolf;
// 合法
if(king < Jumbo)
std::cout << "king is smaller" << std::endl;
// 非法的
if(king < t_shirt::Jumbo)
std::cout << "king is smaller" << std::endl;
但在必要的时候, 可以进行显示类型转换:
int Frodo = int(t_shirt::Small);
可以通过以下方式来指定enum的底层类型:
enum class : short pizza {Samll, Medium, Large, Jumbo};