C++学习笔记(七)

第七章 类

1. 类的基本思想是数据抽象和封装。数据抽象是一种依赖于接口和实现分离的编程技术,类的接口包括用户所能执行的操作;类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。封装实现了类的接口和实现的分离,封装后的类隐藏了它的实现细节,也就是说,类的用户只能使用接口而无法访问实现部分。

2. 成员函数的声明必须在内的内部,定义则可以在内部或者外部。

3. X a()  const { } 紧跟在参数列表后面的const表示this是一个指向常量的指针,这种成员函数成为常量成员函数,因为this指向常量的指针,所以常量成员函数不能改变调用它的对象的内容。比如:

class Screen{
     void a() const;
     size_t b;
}
void Screen::a()const
{
     ++b;
}
此时每次调用函数a,b的值将不会改变;如果想要改变b的值,需要在size_t 前面加上关键字mutable表示b是一个可变数据成员。这样就可以修改b了。

4. 类通过一个或几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做构造函数。构造函数的名字和类名一致,构造函数不能声明为const的。和其他函数的区别是:构造函数没有返回类型。类通过过一个特殊的构造函数来控制默认初始过程,这个函数叫做默认构造函数。默认构造函数无需实参。如果我们的类没有显示地定义构造函数,那么编译器就会为我们隐式的定义一个默认构造函数,成为合成的默认构造函数。只有当类没有声明任何构造函数的时候,编译器才会自动生成默认构造函数。对于一个普通类来说,必须定义他自己的默认构造函数。

5. 定义在public说明符之后的成员在整个程序内可被访问,public成员定义类的接口,构造函数和部分成员函数在public后面;

    定义在private说明符后的成员可以被类的成员访问,但是不能被使用该类的代码访问,private部分封装(隐藏)了类的实现细节。数据成员和作为实现部分的函数在private后面。

    每个访问说明符指定了接下来的成员的访问级别,有效范围知道出现下一个访问说明符或达到类的结尾处为止。

6. class和struct的区别:默认访问权限不一样。

    类可以在第一个访问说明符之前定义成员。如果使用struct,则定义在第一个说明符之前的成员是public的,相反,使用class,这些成员是private的。所以如果希望成员是私有时,使用class。

7.什么是封装,有什么用?    封装的益处:确保用户代码不会无意间破坏封装对象的状态;被封装的类的具体实现细节可以随时改变,而无需调整用户级别的代码。

8. 类可以允许其他类或者函数访问他的非公有成员,方法是令其他类或函数成为它的友元。只需要增加一条friend开后的声明语句即可。友元的声明仅仅指定了访问权限,而非通常意义的声明。

9. 定义在类内部的成员函数是隐式内联的,也可以加上inline显式声明函数为内联。或者在类的外部在函数定义时用inline关键词修饰函数定义。  

10.可以把类的声明和定义分离开,比如声明一个类 class A;  这种声明也被称为前向声明,表示A是一个类类型。对于类型A来说,在他声明之后定义之前是一个不完全类型,意味着知道A是一个类,但不清楚到底包含什么成员。

     对于一个类来说,我们在创建它的对象之前该类必须被定义过而不仅仅被声明过。

11. 类的定义分为两步处理:编译成员的声明;知道类全部可见后才编译函数体。编译器处理完类中的全部声明后才会处理成员函数的定义。

12. 类的作用域:一般处理方式为:函数体内->类内->类外(往前)  .

13. 如果成员函数是const或者引用的话,必须要将其初始化。对其初始化的唯一机会就是通过构造函数初始值。成员初始化的顺序要与类中定义的顺序一致。

14. 委托构造函数使用他所属类的其他构造函数执行它自己的初始化过程,或者说它把它自己的一些或者全部职责委托给了其他构造函数。在委托构造函数内,成员初始值列表只有一个唯一的入口,就是类名本身。

15. 在要求隐式转换的程序中,可通过将构造函数声明为explicit加以阻止,抑制隐式转换。关键字explicit只对一个实参的构造函数有效,需要多个实参的构造函数不能用于执行隐式转换。且只能在类内声明时使用这个关键字,类外不能使用。

16. 聚合类使得用户可以直接访问其成员,并且具有特殊的初始化语法形式。当一个类满足如下条件时,就说它是聚合的:

      所有成员都是public的;没有定义任何构造函数;没有类内初始值;没有基类,也没有virtual函数。 

      可以提供一个花括号括起来的成员值列表,并用它初始化聚合类的数据成员,注意初始值的顺序必须和声明的顺序一致。

17. 类的静态成员:通过在声明前加上关键字static使其与类关联在一起。静态成员函数不与任何对象绑定在一起,它们不包含this指针,作为结果,静态成员不能声明为const的,而且不能在static函数体内使用this指针。

18. 因为静态数据不属于类的任何一个对象,所以它们并不是在创建类的对象时被定义的,这意味着它们不是由类的构造函数初始化,而且不能在类的内部初始化静态成员,必须在类的外部定义和初始化每个静态成员。除此之外,可以为静态成员提供const整数类型的类内初始值,不过要求静态成员必须是constexpr。

c++的静态成员详解 

C++类静态成员与类静态成员函数详解

C++ 类的静态成员(static)

 例:书店程序 

#include<iostream>
#include<string>
using namespace::std;
struct Sales_data;
istream& read(istream &, Sales_data &);                 //Sales_data(istream &s){ read(s, *this); }中调用了read()函数,要提前声明
class Sales_data{
	friend istream& read(istream &, Sales_data &);      //允许read和print访问类的私有成员
 	friend ostream& print(ostream &, Sales_data&);
public:
	Sales_data() = default;                            //默认构造函数。因为既需要其他形式的构造函数,也需要默认形式的构造函数。
	Sales_data(const string &s, unsigned n, double p) :bookNo(s), units_sold(n),revenue(n*p){} //为三个参数初始化的构造函数
	Sales_data(const string &s) :bookNo(s){}                                                   //为一个参数初始化的构造函数
	Sales_data(istream &s){ read(s, *this); }            //构造函数,初始值列表为空,结构体不为空。从is读取一条交易信息后存入this对象中。
	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& Sales_data::combine(const Sales_data&rhs){     //当我们定义的函数类似于某个内置运算符是,应该令该函数的行为模仿这个运算符。
														   //内置的赋值运算符返回左值,因此为了和它一致,combine必须返回引用类型(因为调用一个返回引用的函数可以得到左值)
														   //因为此时左侧对象是一个sales_data的对象,所以返回类型是sales_data& 
	units_sold += rhs.units_sold;
	revenue += rhs.revenue;
	return *this;                                          //把调用函数的对象当成一个整体,return解引用this指针获得执行该函数的对象,因此返回的是total的引用。
}
istream& read(istream &is, Sales_data &item){              //read和print分别接受一个各自的IO类型的引用作为其参数,这是因为IO类属于不能拷贝的类型,只能通过引用来传递他们
	double price = 0.0;
	is >> item.bookNo >> item.units_sold >> price;
	item.revenue = price*item.units_sold;
	return is;
}
ostream& print(ostream &os, Sales_data& item){
	os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();
	return os;
}
int main(){
	Sales_data total(cin);
	if (!total.isbn().empty()){
		istream &is = cin;
		while (is){
			Sales_data trans(is);
			if (total.isbn() == trans.isbn())    //total.isbn()此调用zhongisbn返回bookNo时,实际上隐式返回total.bookNo。                                                          //成员函数通过隐式形参this来访问调用对象,编译器负责把total的地址传递给isbn的隐式形参this。this是一个常量指针。
				total.combine(trans);                         //total的地址被绑定到隐式的this参数上。
			else{
				print(cout, total)<<endl;
				total = trans;
			}
		}
	    print(cout, total) << endl;
	}
	else{
		cerr << "ERROR!" << endl;
	}
	return 0;
}
例:关于委托构造函数

#include<iostream>
#include<string>
using namespace::std;
struct Sales_data;
istream& read(istream &, Sales_data &);
class Sales_data{
	friend istream& read(istream &, Sales_data &);
	friend ostream& print(ostream &, Sales_data&);
public:
	//非委托构造函数使用对应的实参初始化成员
	Sales_data(const string &s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(n*p){ 
	              	cout << "Sales_data(const string &s, unsigned n, double p)" << endl; }
	//其余构造函数全部委托给另一个构造函数
	Sales_data() :Sales_data("", 0, 0){ cout << "Sales_data() " << endl; }          //接收三个实参,用其初始化
	Sales_data(string s) :Sales_data(s, 0, 0){ cout << "Sales_data(string s)" << endl; } //接收一个实参
	Sales_data(istream &is) :Sales_data(){ read(is, *this); }                       //委托给了默认构造函数sales_data,默认构造函数又委托给接收三参数的构造函数,
																		            //当受委托的构造函数执行完后(包括函数体内的输出语句),再执行自己构造体内的输出语句。
	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& 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.0;
	is >> item.bookNo >> item.units_sold >> price;
	item.revenue = price*item.units_sold;
	return is;
}
ostream& print(ostream &os, Sales_data& item){
	os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();
	return os;
}
int main()
{
	cout << "1. default way: " << endl;
	cout << "----------------" << endl;
	Sales_data s1;

	cout << "\n2. use std::string as parameter: " << endl;
	cout << "----------------" << endl;
	Sales_data s2("CPP-Primer-5th");

	cout << "\n3. complete parameters: " << endl;
	cout << "----------------" << endl;
	Sales_data s3("CPP-Primer-5th", 3, 25.8);

	cout << "\n4. use istream as parameter: " << endl;
	cout << "----------------" << endl;
	Sales_data s4(std::cin);

	return 0;
} 
注意程序的流程,运行结果如下。


2016年倒数第二天终于看完了C++ prime part.1。但仅仅是看完了,日后还要多练习。

2017要更努力!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值