C++ primer 学习之旅——第七章 类

第七章 类

再引前缘:旅途开篇C++ primer 学习之旅——迷茫后启程

7.1 实现Sales_data类后的收获与感悟

  • 类的基本思想是数据抽象和封装(private),这也是为什么class的默认权限是private。封装的概念很重要,要重视private,开发中很有用;
  • private理解:定义在private之下的成员可以被类成员函数访问,但不能被其他代码访问(当然友元除外)
  • 一般类的数据成员都封装在private权限中,public权限用于提供函数接口(让用户通过这些函数来与private中的成员打交道)
  • 想单独访问某个数据成员时,可以提供相应的函数接口(没必要放在public作用域下)
inline int Sales_data::show_isbn()
{
	return this->isbn;
}
  • 没有 unsigned double 类型
  • 定义在类内部的函数是隐式inline的。在(头文件中)类中声明函数时不用写inline,因为时默认的(隐式inline);但在函数定义时如果有必要的话(注意是有必要的话,应遵循内联函数规定)应加上inline;注意:内联函数常常定义在(注意,不是声明)头文件中,而不是源文件;
  • istream & ostream
istream& my_read(istream& is,Sales_data& book)
{
	is >> book.isbn >> book.num >> book.value_sum;
	return is;
}

ostream& my_print(ostream& os, const Sales_data& book)
{
	os << ' ' << book.isbn << ' ' << book.num << ' ' << book.value_sum;
	return os;
}
//细节:输出函数应尽量减少对格式的控制(如换行符endl),这样用户代码的灵活度更高

istream 和 ostream 都属于IO类,而IO类属于不能拷贝类型,所以我们只能通过引用的方式传递它们;

  • 大多数情况用 const Sales_data& book,但不要一股脑全用,想清楚const和&什么时候加,什么时候不加;
  • 当private中放有数据成员时,应常把权限放心中(friend勿忘记),不然可能编译报错
  • 编译器分两步处理类:先编译成员的声明,然后才是成员函数。因此,成员函数体可以随意使用其他成员而无须在意这些成员出现的次序。而且成员函数之间的次序也可以随意(即使相互调用)

this指针 与 const成员函数

  • 在成员函数内,任何对类成员的直接访问都被看作对this的隐式引用,即isbn本质上是this->isbn
  • this 是一个常量指针,是 Sales_data* const this
  • const 成员函数
class Student
{
public:
    void show_id() const
    {
        cout << id << endl;
    }
private:
    string id;
};

const 是来修饰this指针的,使得在这个函数中this所指向的成员(不管是隐式还是显示)无法修改

  • 道高一尺,魔高一丈:mutable 关键字。const成员函数可以改变一个可变成员的值
class Student
{
public:
    void change_id(string s) const
    {
        this->id = s;
    }
private:
    mutable string id;
};
  • 如果成员函数声明时加了const,那么它在定义时也必须在参数列表后加上const
  • 可以定义函数使其的返回值为 *this。补充:返回值类型为Student还是Student&是有区别的。前者会拷贝一份 *this,但已不再是从前的那个少年;

构造、析构、拷贝

构造函数(constructor)

  • 构造函数(constructor 英文名也应该知道)出现的意义就是来初始化类对象数据成员的(理解这点很重要)
  • 构造函数可以有多个;
  • 构造函数不能被声明成const的
  • 当程序员未定义构造函数时,编译器会自己补一个默认构造函数(无参,且函数体为空,即数据成员未通过构造函数进行初始化)
  • 除了构造函数进行初始化外,还可以显示的进行初始化
class Student
{
private:
    int age = 17;
    string school("八中");
};
  • 有一个点需要注意:在创建新类时,尽量确保每个值都能被初始化(不管用哪种方式),不然值是未定义的,很危险!
  • 当定义了自己的构造函数后,编译器将不再提供默认构造函数,这时,可能会出一些错误
class Student
{
public:
    Student(int age,string school)
    {
        this->age = age;
        this->school = school;
    }
private:
    int age ;
    string school;
};

int main()
{
    Student student; //错误,会报错,应为没有默认无参构造函数
    Student student2(17,"八中"); //正确
    return 0;
    
    //有人可能有这样的疑惑,为什么想调用无参构造时,不写成这样:
    //Student student();
    //因为编译器不知道你这是函数调用(或函数声明)还是创立新对象
}
  • =default的含义:就是默认构造的一种高颜值写法;
//为了防止上面这种错误发生,常常需要自己补一个默认构造或无参构造
class Student
{
public:
    Student() = default;
    Student(int age,string school)
    {
        this->age = age;
        this->school = school;
    }
private:
    int age ;
    string school;
};
  • 构造函数也可以定义在类外部,加作用域即可;

拷贝构造函数

  • 默认情况下,编译器会帮我们自动生成拷贝构造函数,大概长成这样:
Student(const Student& s)
{
    this->age = s->age;
    this->school = s->school;
}
  • 也正是有了拷贝构造函数,以下代码才合法:
Student s1(17,"八中");
Student s2 = s1; //等同于 Student s2(s1);

补充:函数返回时,也是类似于 '='赋值

访问控制与封装

  • 什么样的成员应该放在public说明符下,哪些又应放在private下?
//一般来说,作为接口的一部分、构造函数和部分成员函数应该定义在public下;
//而数据成员和作为实现部分的函数应该放在private下

友元

  • 作用:使类外函数也能访问到private权限下的成员
  • 语法
class Student
{
    friend int get_age(const Student& student);
public:
    Student() = default;
    Student(int age,string school)
    {
        this->age = age;
        this->school = school;
    }
private:
    int age ;
    string school;
};

int get_age(const Student& student)
{
    return student.age;
}
  • 友元声明只能出现在类的内部,但具体在类的哪个位置,没有明确要求。但一般集中放在类的开始或结束位置
  • 友元声明仅仅指定了访问的权限,而非通常意义上的函数声明。虽然有的编译器不强制类外应该有友元函数的正式声明,但我们最好加上。即最好在类外先有函数的声明,然后再在类内声明友元;

我的代码仓库:我的仓库

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值