《C++PrimerPlus 6th Edition》第10章 对象和类 要点记录


三个月没更了,继续走起~

本章内容

  1. 过程性编程和OOP
  2. 类概念、类定义和实现
  3. 公有类访问和私有类访问
  4. 类的数据成员
  5. 类方法(类函数成员)
  6. 创建和使用类对象
  7. const成员函数
  8. this指针
  9. 创建对象数组
  10. 类作用域
  11. 抽象数据类型
10.1 过程性编程与面向对象编程
  1. OOP的主要目标之一:数据隐藏。因此,数据项通常放在私有部分,组成类接口的成员函数放在公有部分;否则,就无法从程序中调用这些函数。
10.2 抽象与类
  1. 内联方法:其定义位于类声明中的函数都将自动成为内联函数。当然,也可以在类声明之外定义成员函数,并使其成为内联函数。为此,只需类实现部分(声明时不用)中定义函数时使用inline限定符即可。
  2. 类数据成员与类函数成员的区别:对于内部变量和类成员,每个类对象都有自己的存储空间;但同一个类的所有对象共享同一组类方法,即每种方法只有一个副本。
  3. 要创建类对象,可以声明类变量,也可以使用new为类对象分配存储空间。可以将对象作为函数的参数和返回值,也可以将一个对象赋给另一个。
  4. 对于特定格式设定的类函数,设计要旨为:将格式修改限定在实现文件中,以免影响程序的其他方面。给个模板自行体会:
    void Stock::show(){
          
          
    	std::streamsize prec = std::cout.precision(3); //old value
    	//...
    	std::cout.precision(prec); //reset to old value
    	//store original flags
    	std::ios_base::fmtflags orig = std::cout.setf(std::ios_base::fixed);
    	//...
    	//reset to stored value
    	std::cout.setf(orig, std::ios_base::floatfield);
    }
    
10.3 类的构造函数和析构函数
  1. 类数据部分的访问状态通常 是私有的(为了符合OOP的要旨),这意味着程序不能直接访问数据成员,从而只能通过成员函数来访问数据成员,因此需要构造函数将对象成功地初始化。

  2. 构造函数的参数表示的不是类成员,而是赋给类成员的值。因此,参数名不能与类成员相同,一种做法是在类数据成员名中使用m_前缀后缀_

    class A{
          
          
    	private:
    		string m_name;
    		long m_money;
    };
    class B{
          
          
    	private:
    		string name_;
    		long money_;
    };
    
  3. 构造函数的使用

    • 显式调用。 Stock food = Stock(para1, para2, ...);
    • 隐式调用。 Stock food(para1, para2, ...);
    • 动态分配。 Stock *pstock = new Stock(para1, para2, ...);

    注:无法使用对象来调用构造函数,因为在构造函数构造对象前,对象是不存在的。

  4. 默认构造函数

    • 在未提供显式初始值时,用来创建对象的构造函数。Stock a; //调用默认构造函数
    • 当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数。为类定义构造函数后,程序员就必须为它提供默认构造函数。如果提供了非默认构造函数,但没有提供默认构造函数,则下面的声明将出错: Stock stock1;
    • 定义默认构造函数的两种方式:①给已有构造函数的所有参数提供默认值: Stock(const string& co="Error", int n=0, double pr=0.0);;②通过函数重载定义另一个构造函数——一个没有参数的构造函数:Stock();。以上方法不能同时采用,因为只能有一个默认构造函数。
      注:在设计类时,通常应提供对所有类成员做隐式初始化的默认构造函数。
    • 隐式调用默认构造函数时,不要使用圆括号:
      Stock first(para1, para2); //调用构造函数
      Stock second(); //声明一个以Stock为返回类型的函数
      Stock third; //隐式调用默认构造函数
      
  5. 析构函数:原型只能为:~Stock()

  6. 关于构造函数与析构函数的调用时机,书上有个例子,看着就能理解了

    #include<iostream>
    #include "stock10.h"
    int main(){
          
          
    	{
          
          
    		using std::cout;
    		cout<<"Using constructors to create new objects\n";
    		Stock stock1("NanoSmart", 12, 20.0); //syntax1
    		stock1.show();
    		Stock stock2 = Stock("Boffo Objects", 2, 2.0); //syntax2
    		stock2.show();
    		
    		cout<<"Assigning stock1 to stock2:\n";
    		stock2 = stock1; 
    		cout<<"Listing stock1 and stock2:\n";
    		stock1.show();
    		stock2.show();
    		
    		cout<<"Using a constructor to reset an object\n";
    		stock1 = Stock("Nifty Foods", 10, 50.0); //临时对象
    		cout<<"Revised stock1:\n";
    		stock1.show();
    		cout<<"Done!\n"; 
    	}
    	return 0;
    }
    

    解读:

    1. 大花括号的作用是便于在main()返回前能看见析构函数的调用。
    2. 对于syntax2,有两种方式来执行:①与syntax1相同,创建一个对象,把其数据成员初始化为指定的值;②允许调用构造函数来创建一个临时对象,然后将该临时对象复制到stock2中,并丢弃它。因此,丢弃临时对象时会执行临时对象的析构函数。
    3. 与结构赋值一样,在默认情况下,给类对象赋值时,将把一个对象的每个数据成员复制给另一个。
    4. 有些编译器可能要过一段时间才删除临时对象,因此析构函数的调用将延迟。
    5. 最先创建的对象将会最后被删除,这是因为自动变量放在了栈中。
    6. 以下两条语句有根本性的区别:
      Stock stock2 = Stock("Boffo Objects", 2, 2.0);
      stock1 = Stock("Nifty Foods", 10, 50.0);
      
      第一句是初始化,它创建指定值的对象,可能会创建临时对象(也可能不);第二条是赋值,它总会导致在赋值前创建一个临时对象,因此为了高效率尽量采用初始化方式。
  7. C++11列表初始化:Stock hot_tip = {"Derivativess Plus Plus", 100, 45.0};

  8. const成员函数:放在成员函数括号的后面,用于保证函数不会调用修改对象。如void show() const;,同样其函数定义的开头应为:void Stock::show() const;,只要方法不修改调用对象,就应将其声明为const。

  9. 小结:

    • 构造函数没有声明类型
    • 如果构造函数只有一个参数,则将其对象初始化为一个与参数类型相同的值时,该构造函数将被调用:
      Bozo dribble = Bozo(44); // primary form
      Bozo roon(66); // secondary form
      Bozo tubby = 32; // special form for one-argument constructors
      
      第11章将介绍一种关闭这项特性的方式,因为它可能带来令人不愉快的意外。
    • 如果构造函数使用了new,则必须提供使用delete的析构函数。
10.4 this指针
  1. this指针指向用来调用成员函数的对象(this被作为隐藏参数传递给方法)。
  2. 注意,要返回类对象时,应为*this,而非this,后者是对象的地址。
10.5 对象数组
  1. 初始化对象数组的方案:首先使用默认构造函数创建数组元素,然后花括号中的构造函数将创建临时对象,然后将临时对象的内容复制到相应的数组元素中。因此,要创建类对象数组,则这个类必须有默认构造函数
10.6 类作用域
  1. 在类中直接用const声明数据成员是行不通的,因为在创建对象前,将没有用于存储值的空间。
  2. 两种创建作用域为类的常量的方法:
    • 在类中声明一个枚举enum {Months = 12};,这种方式声明枚举不会创建类数据成员。也就是说,所有对象都不包含枚举。另外,Months只是个符号名称,在作用域为整个类的代码中遇到它时,编译器将用12替换它。由于使用枚举只是为了创建符号常量,并不打算创建枚举类型的变量,因此不需要提供枚举名。
    • 使用关键字staticstatic const int Months = 12; 这将创建一个名为Months的常量,该常量将与其他静态变量存储在一起,而不是存储在对象中。
  3. 作用域枚举(C++11):(用struct代替class也行,但无论如何都需要使用枚举名来限定枚举量)
    enum class egg {
          
          Small, Medium, Large, Jumbo}
    enum class t_shirt {
          
          Small, Medium, Large, Xlarge}
    egg choice = egg::Large;
    t_shirt playboy = t_shirt::Large;
    
    作用:设置枚举的作用域为类,将eggt_shirt枚举的作用域区分开来,避免了冲突。
    另外,C++11还提高了枚举的类型安全。有些情况下,常规枚举可以自动转换为整型,但作用域内枚举不能隐式地转换为整型,不过在必要时可以进行显式类型转换。
    C++11中,枚举的底层整型类型默认为int,但也可以自主做选择:
    enum class : short pizza{
          
          Small, Medium, Large, Xlarge};
    
10.7 抽象数据类型(Abstract Data Type, ADT)
  1. 以栈为例举了个例子,数据结构中也提过。

待深入的问题

  • 类的头文件、类定义文件以及类使用文件之间的链接方式

习题

见我的github(上传后会把链接打上)

欢迎各位大佬们于评论区进行批评指正~


上一篇文章:《C++PrimerPlus 6th Edition》第9章 内存模型与名称空间 要点记录

下一篇文章:《C++PrimerPlus 6th Edition》第11章 使用类 要点记录

猜你喜欢

转载自blog.csdn.net/weixin_42430021/article/details/109147634