#ifndef SALES_DATA_H
#define SALES_DATA_H
#include "Version_test.h"
#include <string>
#include <iostream>
class Sales_data {
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::ostream &print(std::ostream&, const Sales_data&);
friend std::istream &read(std::istream&, Sales_data&);
public:
// constructors
// using the synthesized version is safe only
// if we can also use in-class initializers
#if defined(IN_CLASS_INITS) && defined(DEFAULT_FCNS)
Sales_data() = default;
//我们定义这个构造函数的目的仅仅是我们既需要其他形式的构造函数,也需要默认构造函数,我们希望这个函数的作用与默认构造函数的行为一致,因而使用default关键字。内部成员会根据类内初始值进行初始化。
#else
Sales_data(): units_sold(0), revenue(0.0) { }
#endif
#ifdef IN_CLASS_INITS
Sales_data(const std::string &s): bookNo(s) { }
#else
Sales_data(const std::string &s):
bookNo(s), units_sold(0), revenue(0.0) { }
#endif
Sales_data(const std::string &s, unsigned n, double p):
bookNo(s), units_sold(n), revenue(p*n) { }
Sales_data(std::istream &);
// operations on Sales_data objects
std::string isbn() const { return bookNo; }
//跟在形参列表之后的const的作用是修改隐式this指针的类型,在通常情况下,this是一个顶层const指针,这意味着this不能直接绑定到一个常量对象上,故而不能在一个常量对象上调用普通的成员函数。c++的补偿方法是在形参列表之后加入const以说明this指针指向的是一个常量,这样的函数被称为常量尘缘函数。
//常量对象,常量对象的引用与指针都只能调用常量成员函数
Sales_data& combine(const Sales_data&);
double avg_price() const;
private:
std::string bookNo;
#ifdef IN_CLASS_INITS // using the synthesized version is safe only
unsigned units_sold = 0;
double revenue = 0.0;
#else
unsigned units_sold;
double revenue;
#endif
};
// nonmember Sales_data interface functions
Sales_data add(const Sales_data&, const Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);
// used in future chapters
inline
bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs)
{
return lhs.isbn() < rhs.isbn();
}
#endif
//如果vector与string包含是一个类的成员,那么默认的复制与析构函数可以正常的分配与回收空间
#include "Version_test.h"
#include <string>
#include <iostream>
class Screen {
public:
typedef std::string::size_type pos;
#if defined(IN_CLASS_INITS) && defined(DEFAULT_FCNS)
Screen() = default; // needed because Screen has another constructor
#else
Screen(): cursor(0), height(0), width(0) { }
#endif
// cursor initialized to 0 by its in-class initializer
Screen(pos ht, pos wd, char c): height(ht), width(wd),
contents(ht * wd, c) { }
friend class Window_mgr;
Screen(pos ht = 0, pos wd = 0):
cursor(0), height(ht), width(wd), contents(ht * wd, ' ') { }
char get() const // get the character at the cursor
{ return contents[cursor]; } // implicitly inline
inline char get(pos ht, pos wd) const; // explicitly inline
Screen &clear(char = bkground);
private:
static const char bkground = ' ';
public:
Screen &move(pos r, pos c); // can be made inline later
Screen &set(char);
Screen &set(pos, pos, char);
// other members as before
// display overloaded on whether the object is const or not
Screen &display(std::ostream &os)
{ do_display(os); return *this; }
const Screen &display(std::ostream &os) const
{ do_display(os); return *this; }
//const版本的display存在的一个缺陷在于其返回的是一个const对象,意味着我们
//无法将一系列动作嵌入到代码中(screen.display.set)
//因而我们在const重载的基础上加了非const的display;
private:
// function to do the work of displaying a Screen
void do_display(std::ostream &os) const {os << contents;}
// other members as before
private:
#ifdef IN_CLASS_INITS
pos cursor = 0;
pos height = 0, width = 0;
#else
pos cursor;
pos height, width;
#endif
std::string contents;
};
Screen &Screen::clear(char c)
{
contents = std::string(height*width, c);
return *this;
}
inline // we can specify inline on the definition
Screen &Screen::move(pos r, pos c)
{
pos row = r * width; // compute the row location
cursor = row + c; // move cursor to the column within that row
return *this; // return this object as an lvalue
}
char Screen::get(pos r, pos c) const // declared as inline in the class
{
pos row = r * width; // compute row location
return contents[row + c]; // return character at the given column
}
inline Screen &Screen::set(char c)
{
contents[cursor] = c; // set the new value at the current cursor location
return *this; // return this object as an lvalue
}
inline Screen &Screen::set(pos r, pos col, char ch)
{
contents[r*width + col] = ch; // set specified location to given value
return *this; // return this object as an lvalue
}
//如果对象是一个const对象而希望其中有可变化的量,那么就在变量描述符上加入mutable,表示该成员永远不会是const
//尽管内层作用域可以重定义外层的变量
typedef double Money;
class Account
{
public:
Money balance()
{
return bal;
}
private:
typedef double Money;
//错误:不能重新定义Money
Money bal;
};
对于一个类,其搜索一个名字的过程是:函数内-》类内-》类之外包含着类的域(如果函数定义在类体外,还要考虑函数定义之前的部分)
如果可以,应当尽量使用构造函数初始值(列表初始化)。随着构造函数体的一开始执行,所有变量的初始化就完成了,因而对于const成员来说,列表初始化是其唯一的初始化方法与入口。
列表初始化的顺序与列表中各变量的顺序无关,只与在类体时定义的顺序有关。
Sales_data():Sales_data("",0,0){}
//C++11支持委托构造函数功能,即通过其他的构造函数功能来实现初始化。
class myint
{
private:
int val;
public:
myint():myint(0){}
myint(int val_):val(val_){}
void combine(myint another)
{
val += another.val;
cout << val << flush;
}
};
int main()
{
myint val0 = 1;//正确,实现了隐式转换。
myint val(2);
val.combine(3);
//输出5
return 0;
}
//如果构造函数只接受一个实参,那么实际上它定义了转换为此类型的隐式转换机制。这种机制只能实现一步转换(比如一个类myclass定义了一个接受string的构造函数与一个接受同一个类另一个对象的函数func,那么在func函数中直接输入一个字符串常量时不能实现转换的,需要先强制转换为string或类myclass才可以运行函数)
//如果要抑制这种转换,需要在相应的构造函数之前加入explicit
//当我们用explicit声明函数时,我们不能使用带有=的初始化的语句(这种语句时最有可能发生隐式转换的),而只能使用直接初始化的句子
//假设前面的构造函数加入了explicit
myint val0 = 1;//错误,不能将explicit函数用于拷贝形式的初始化过程
myint val(2);//正确
//尽管隐式转换被抑制,但显式的转换被保留下来
myint val1 = static_cast<myint>(4);
在标准库中,string不是explicit的(可以接受字符串常量,支持=),vector则不是。
#include "Version_test.h"
#ifndef DEBUG_H
#define DEBUG_H
class Debug {
public:
#ifdef CONSTEXPR_CTORS
constexpr Debug(bool b = true): hw(b), io(b), other(b) { }
constexpr Debug(bool h, bool i, bool o):
hw(h), io(i), other(o) { }
constexpr bool any() { return hw || io || other; }
constexpr bool hardware() { return hw || io; }
constexpr bool app() { return other; }
//一个有趣的事实是,构造函数既不能返回任何值,又要满足constexpr的要求,因而,被constexpr修饰的构造函数函数体一般为空
#else
Debug(bool b = true): hw(b), io(b), other(b) { }
Debug(bool h, bool i, bool o):
hw(h), io(i), other(o) { }
bool any() const { return hw || io || other; }
bool hardware() const { return hw || io; }
bool app() const { return other; }
#endif
void set_io(bool b) { io = b; }
void set_hw(bool b) { hw = b; }
void set_other(bool b) { hw = b; }
private:
bool hw; // hardware errors other than IO errors
bool io; // IO errors
bool other; // other errors
};
class HW_Subsystem {
public:
HW_Subsystem(): debug(false) { } // by default no debugging
bool field_debug() { return debug.any(); }
bool default_debug() { return enable.any() && debug.any(); }
void set_debug(bool b) { debug.set_hw(b); } // turn on hardware debugging
private:
Debug debug;
#ifdef CONSTEXPR_CTORS
constexpr static Debug enable{true, false, false};
#else
static const Debug enable;
#endif
};
class IO_Subsystem {
public:
IO_Subsystem(): debug(false) { } // by default no debugging
bool field_debug() { return debug.any(); }
bool default_debug() { return enable.any() && debug.any(); }
void set_debug(bool b) { debug.set_io(b); } // turn on IO debugging
private:
Debug debug;
#ifdef CONSTEXPR_CTORS
constexpr static Debug enable{true, false, true};
#else
static const Debug enable;
#endif
};
#endif
//有一些类可以被称为字面值常量类,它们至少包含一个被constexpr修饰的构造函数,并且数据成员均为字面值类型,并且使用默认的析构函数。
1.如果一个成员变量拥有类内初始值,那么初始值必须是常量表达式。
2.成员属于某种类类型,那么初始值必须使用成员自己的constexpr构造函数。
//静态成员变量
#include <stdio.h>
#include <string.h>
const int MAX_NAME_SIZE = 30;
class Student
{
public:
Student(char *pszName);
~Student();
public:
static void PrintfAllStudents();
private:
char m_name[MAX_NAME_SIZE];
Student *next;
Student *prev;
static Student *m_head;
static int num;
};
int Student::num = 0;
Student::Student(char *pszName)
{
strcpy_s(this->m_name, pszName);
//建立双向链表,新数据从链表头部插入。
this->next = m_head;
this->prev = NULL;
if (m_head != NULL)
m_head->prev = this;
m_head = this;
++num;
}
Student::~Student()//析构过程就是节点的脱离过程
{
if (this == m_head) //该节点就是头节点。
{
m_head = this->next;
}
else
{
this->prev->next = this->next;
this->next->prev = this->prev;
}
}
void Student::PrintfAllStudents()
{
for (Student *p = m_head; p != NULL; p = p->next)
printf("%s\n", p->m_name);
printf("%d\n", num);
}
Student* Student::m_head = NULL;
void main()
{
Student studentA("AAA");
Student studentB("BBB");
Student studentC("CCC");
Student studentD("DDD");
Student student("MoreWindows");
Student::PrintfAllStudents();
}
//实际上,constexor静态成员可以使用类内初始化,不过这样的话,也应当在类外定义一下该成员。
#include <stdio.h>
#include <string.h>
const int MAX_NAME_SIZE = 30;
class Student
{
public:
Student(char *pszName);
~Student();
public:
static void PrintfAllStudents();
private:
char m_name[MAX_NAME_SIZE];
Student *next;
Student *prev;
static Student *m_head;
constexpr static int num=0;
};
constexpr int Student::num;
Student::Student(char *pszName)
{
strcpy_s(this->m_name, pszName);
//建立双向链表,新数据从链表头部插入。
this->next = m_head;
this->prev = NULL;
if (m_head != NULL)
m_head->prev = this;
m_head = this;
}
Student::~Student()//析构过程就是节点的脱离过程
{
if (this == m_head) //该节点就是头节点。
{
m_head = this->next;
}
else
{
this->prev->next = this->next;
this->next->prev = this->prev;
}
}
void Student::PrintfAllStudents()
{
for (Student *p = m_head; p != NULL; p = p->next)
printf("%s\n", p->m_name);
printf("%d\n", num);
}
Student* Student::m_head = NULL;
void main()
{
Student studentA("AAA");
Student studentB("BBB");
Student studentC("CCC");
Student studentD("DDD");
Student student("MoreWindows");
Student::PrintfAllStudents();
}