一、所有成员都必须在类的内部声明
二、成员函数体可以定义在类内也可以定义在类外
三、成员函数通过一个名为this的额外的隐式参数来访问调用它的那个对象,当我们调用一个成员函数时,用请求该函数的对象地址初始化this
四、在成员函数内部,我们可以直接使用调用该函数的对象的成员,而无须通过成员访问运算符来做到这一点,因为this所指的正是这个对象,任何对类成员的直接访问都被看作this的隐式引用
五、任何自定义名为this的参数或变量的行为都是非法的
六、因为this的目的总是指向对象,所以this是一个常量指针
七、默认情况下,this的类型是指向类类型非常量版本的常量指针
八、紧跟在参数列表后的const表示this是一个指向常量的指针,被称为常量成员函数
九、编译器分2步处理类:1、编译成员的声明;2、编译成员函数体
十、当我们在类的外部定义函数时,成员函数的定义必须与它的声明匹配,返回类型、参数列表、函数名都需与类内部的声明一致,如果成员被声明成常量成员函数,那么它的定义也必须在参数列表后明确指定const属性
十一、类外部定义的成员的名字必须包含它所属的类名
十二、默认情况下,拷贝类的对象其实拷贝的是对象的数据成员
十三、构造函数不能被声明成const,当我们创建类的要给const对象时,直到构造函数完成初始化过程,对象才能真正取得其“常量”属性
十四、编译器创建的构造函数又被称为合成的默认构造函数
十五、在c++11中,如果我们需要默认的行为,那么可以通过在参数列表后面加上 =default来要求编译器生成构造函数,如果=default在类的内部,则默认构造函数是内联的
十六、一般来说,编译器生成的版本将对对象的每个成员执行拷贝、赋值和销毁操作
十七、struct和class的唯一区别是访问权限不太一样,使用struct关键字,则定义在第一个访问说明符之前的成员是public的,使用class关键字,则是private的
十八、类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数称为它的友元
十九、友元声明只能出现在类定义的内部
二十、友元的声明仅仅指定了访问的权限,如果希望类的用户能够调用某个友元函数,那么必须在友元申明之外再专门对函数进行一次声明
二一、即使是在一个const成员函数内,也希望可以修改类的某个数据成员,可以通过在变量的声明中加入mutable关键字,被称为可变数据成员
二二、类内初始值可以使用=的初始化形式,也可以使用花括号的形式
二三、类在它申明之后定义之前是一个不完全类型
二四、一旦遇到了类名,定义的剩余部分就在类的作用域中了
二五、函数的返回类型通常出现在函数名之前,因此当成员函数定义在类外部时,返回类型中使用的名字都位于类的作用域外,因此,返回类型必须指明它所属的类
二六、成员的初始化顺序与他们在类定义中出现的顺序一致,构造函数初始值列表中初始值的前后关系不会影响实际的初始化顺序
二七、一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程
二八、如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,称作转换构造函数
二九、explicit关键字只对一个实参的构造函数有效,只能在类内声明构造函数时使用explicit关键字,其作用是阻止构造函数的隐式转换机制
三十、聚合类
1、所有成员都是public的
2、没有定义任何构造函数
3、没有类内初始值
4、没有基类
三一、尽管构造函数不能是const的,但可以是constexpr的, constexpr构造函数必须初始化所有数据成员
三二、静态成员函数不包含this指针,也不能声明成const
三三、当在类外部定义静态成员时,不能重复static关键字,该关键字只出现在类内部的声明语句
三四、静态数据成员可以是不完全类型
三五、静态数据成员的类型可以就是它所属的类类型,而非静态数据成员则受到限制,只能声明成它所属类的指针或引用
class Sales_data {
friend Sales_data add(const Sales_data&, const Sales_data&);
friend ostream& print(ostream&, const Sales_data&);
friend istream& read(istream&, Sales_data&);
public:
Sales_data() = default;
// 如果一个构造函数只接收1个实参,则它实际上定义了转换为此类类型的隐式转换机制,称为转换构造函数
Sales_data(const string& s) : bookNo(s) {};
// 关键字explicit只对一个实参的构造函数有效
// 使用了关键字explicit, 不能再隐式转换
//explicit Sales_data(const string& s) : bookNo(s) {};
Sales_data(const string& s, unsigned n, double p) : bookNo(s), units_sold(n), revenue(p * n) {};
Sales_data(istream&);
string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data&);
private:
double avg_price() const { return units_sold ? revenue / units_sold : 0; }
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
// 友元函数仅仅指定了访问权限,而非一个通常意义上的函数声明
// 如果希望能够调用某个友元函数,就必须在友元声明之外再专门对函数进行一次声明
Sales_data add(const Sales_data&, const Sales_data&);
ostream& print(ostream&, const Sales_data&);
istream& read(istream&, Sales_data&);
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
istream& read(istream& is, Sales_data& item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
ostream& print(ostream& os, const Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();
return os;
}
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
Sales_data::Sales_data(istream& is)
{
read(is, *this);
}
int main()
{
Sales_data item;
string null_book = "9-999-9999-9";// 编译器用给定的string自动创建了一个Sales_data对象
item.combine(null_book); // 如果单参数构造函数加了explicit关键字则编译错误
// 只允许一步类类型转换
//item.combine("9-999-9999-9"); // 错误
item.combine(string("9-999-9999-9"));// 显式的转换为string,隐式转换为Sales_data,如果单参数构造函数加了explicit关键字则编译错误
item.combine(Sales_data("9-999-9999-9")); // 隐式转换为string,显示转换为Sales_data
return 0;
}
class Screen
{
friend class Window_mgr;
public :
// typedef std::string::size_type pos;
using pos = std::string::size_type;
Screen() = default;
Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht* wd, c) {}
char get() const { return contents[cursor]; }
inline char get(pos ht, pos wd) const;
Screen& move(pos r, pos c);
//void some_member() const;
Screen& set(char);
Screen& set(pos, pos, char);
Screen& display(std::ostream& os)
{
do_display(os);
return *this; // 将this作为左值返回,意味着函数返回的是对象本身而非对象的副本
}
const Screen& display(std::ostream &os) const
{
do_display(os);
return *this;// 从const返回this,此时,this是一个指向const的指针二*this是const对象
}
private:
pos cursor = 0;
pos height = 0;
pos width = 0;
std::string contents;
//mutable size_t access_ctr; // 可变数据成员, 即使是一个const成员函数内,也可以被修改
void do_display(std::ostream& os) const { os << contents; }
};
inline
Screen& Screen::move(pos r, pos c)
{
pos row = r * width;
cursor = row + c;
return *this;
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
inline Screen& Screen::set(pos r, pos col, char c)
{
contents[r * width + col] = c;
return *this;
}
char Screen::get(pos r, pos c) const
{
pos row = r * width;
return contents[row + c];
}
/*void Screen::some_member() const
{
++access_ctr;
}*/
class DcClass
{
public:
DcClass(int a, int b, int c) : mA(a), mB(b), mC(c) {}
DcClass() : DcClass(1, 2, 3) {} // 委托构造函数, 默认构造函数
DcClass(int a) : DcClass(a, 2, 3) {} // 委托构造函数
DcClass(double d) : DcClass() { std::cout << d; } // 委托构造函数, 委托给了默认构造函数,默认构造函数又委托给了三参数构造函数
private:
int mA;
int mB;
int mC;
};
class X {
int i;
int j;
public:
// 成员的初始化顺序与他们在类定义中出现的顺序一致
// 构造函数初始值列表中初始值额前后位置关系不会影响实际额初始化顺序
X(int val) : j(val), i(j) {}
void display() const
{
std::cout << i << "-" << j << std::endl;
}
};
class Bar
{
private:
// 在类声明之后定义之前是一个不完全类型
static Bar mem1; // 静态成员可以是不完全类型
Bar* mem2;// 指针成员可以是不完全类型
Bar& mem3;// 引用成员可以是不完全类型
//Bar mem4; // 错误,数据成员必须是完全类型
};
int main()
{
X x(1); // -xxxxxxx-1 i先被初始化,因此这个初始值的效果是试图使用未定义的j初始化i
x.display();
Screen myScreen(5, 3, 'b');
const Screen blank(5, 3, 'c');
myScreen.set(2, 2, '#').display(std::cout);
blank.display(std::cout);
return 0;
}