C++ Primer Plus书之--C++ this指针和类作用域及作用域内枚举

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

猜你喜欢

转载自blog.csdn.net/c1392851600/article/details/84947771