类---C++ 基础

来源:互联网 发布:js音乐播放器插件 编辑:程序博客网 时间:2024/06/05 03:14

前言

不知不觉,已经复习到了类。面向对象编程是C++的一个重要特征。类是对数据和操作的封装,相对于函数,提供了更高层次的抽象。

例子:

struct Sales_data {    std::string isbn() const { return bookNo;}    Sales_data &combine(const Sales_data &);    double avg_price() const;    std::string bookNo;    unsigned int units_sold = 0;    double revenue = 0.0;}; // 类后面的分号不要忘记

定义在类内部的函数是隐式的inline函数。

1.引入this

当我们在调用对象的成员函数时, 其实传入了名为 this 的隐式参数,而这个参数是一个指向调用对象的地址。

total.isbn();// 隐式地传入this指针Sales_data::isbn(&total);

重要的是,this 总是指向“这个”对象,因此,this 是一个常量指针 Sales_data *const。

2.const成员函数

    string isbn() const {} 

就是一个常量成员函数,常量成员函数的意义在于this指针是一个指向常量对象的常量指针。因此在函数中不能改变它对象内容。并且对于常量对象来说,是不能将其转换成一个普通的this指针的,需要使用指向常量对象的指针才行。

注意:

常量对象,常量对象的引用或者指针,只能访问常量成员函数

编译器分两步处理类:首先编译成员的声明,然后在编译成员函数。因此成员函数可以随意使用类成员变量,而无需在意成员的声明次序。

类外定义常量成员函数,必须要还要加上const

3.定义类相关的非成员函数

类常常需要辅助函数,它们不属于类本身,但是类的接口的组成部分。一般的,将他的函数声明和类的声明放在一个文件中。


4.构造函数

构造函数非常重要,它定义了类的初始化方式。只要类的对象被创建,就会执行构造函数。

构造函数不能声明成const。当我们在创建const对象时,直到构造函数完成,对象才真正获得“常量”属性。

  • 默认构造函数,没有任何实参。如果没有定义构造函数,编译器会生成一个默认的构造函数。如果定义了构造函数,编译器就不会产生默认的构造函数。另外,默认函数执行默认初始化,如果内置类型没有默认初始化,那编译器就不能产生默认构造函数。
struct Sales_data {    Sales_data() = default;    Sales_data(const std::string &s) : bookNo(s) {} }; // 一个默认构造函数,一个非默认构造函数

= default 的目的是要求编译器生成默认构造函数

注意:

默认构造函数,首先看是否有类内初始化值,有的话执行初始化,然后才是默认初始化。如果编译器不支持类内初始值,那么默认构造函数就应该使用初始化列表。

  • 初始化列表
Sales_data(const std::string &s) : bookNo(s) {} //初始化的顺序,和成员在类内的声明次序有关,和初始化列表的顺序无关

初始化列表在构造函数体执行之前执行,因此在写构造函数的时候要注意数据成员的初始值。

注意:

如果编译器不支持类内初始值,那每个构造函数都应该使用初始化列表显示初始化。

5.拷贝、赋值和析构

这些过程需要我们控制,虽然编译器会替我们合成它们。但是编译器合成的版本能力有限。

很多需要动态内存的类,应该使用vector,string对象来管理必要的存储空间,这样能够避免分配和释放内存带来的复杂性。

6.访问控制和封装

class和struct的唯一区别在于,class默认权限是private,而struct默认权限是public

7.友元

友元分为友元类和友元函数(包括类的成员函数也可以单独声明成友元)。友元不具有传递性,友元声明只能出现在类内部

声明为友元的可以访问非共有成员,即protect、private。

class Sales_data {    friend Sales_data add(const Sales_data &, const Sales_data &);  };

一般最好在类定义的开始或者结尾集中声明友元

注意:

友元仅仅是指定了访问的权限,而非一个通常意义上的函数声明,因此我们必须在友元声明之外再专门进行一次声明。

8.类的其他特性

  • 定义一个类型成员
class Screen {public:    using pos = std::string::size_type;};
  • 可变数据成员
class Screen {private:    mutable pos s; // 即使在const成员函数中,也能够修改,它永远不会是const};
  • 类内初始值(有些编译器不支持)
class A {private:    std::vector<Screen> screens{Screen(24, 80, ' ')};    int i = 10; };
  • 返回*this的成员函数

这里需要注意的是常量成员函数返回*this引用是一个常量引用,这个时候作为左值来使用的话是不能修改对象的。

解决方法是:定义一个非常量版本的成员函数(重载)。非常量版本是一个更好的匹配,作为左值来使用就会调用它。

9.类的作用域

class Window_mgr {public:    using ScreenIndex = int;    ScreenIndex addScreen(const Screen &);  };// 第二个Window_mgr:: 才进入Window_mgr 的作用域,因此返回类型需要加上作用域Window_mgr::ScreenIndex Window_mgr::addScreen(const Screen &s) {}

类内类型别名要一开始就定义好

typdef double Money;class Account {public:    Money balance() {return bal;} // 使用外层作用域的Money,已经使用了外层的,内层就不能再定义    private:    typdef double Money; // 错误:不能重复定义Money    Money bal;};

10.初始化过程

  • 没有在初始化列表中的,默认初始化
  • 初始化列表初始化
  • 构造函数体内(这个时候准确的来说是赋值)

const 成员或者 引用必须在初始化列表中初始化

初始化的顺序和数据成员声明的顺序一致。

11.一个低级错误

Sales_data obj(); // 错误,这个是一个函数声明,并不是对象Sales_data obj; // 正确,这个是一个对象

12.隐式类型转换

这个在编程中一定要切记,接受一个实参的构造函数,也叫转换构造函数。

转换构造函数定义了一条从构造函数参数类型向类类型隐式转换的规则。

string null_book = "9999";// 构造了一个临时的对象,从string对象隐式转换到Sales_data对象item.combine(null_book);

但是,编译器只允许一步类类型的转换。

想要抑制构造函数定义的隐式转换,要将构造函数声明为explicit

class Sales_data {public:    Sales_data() = default;    //抑制隐式转换    explicit Sales_data(const std::string &s) : bookNo(s) {}    };// explicit 只能用于直接初始化Sales_data item(null_book); // 正确Sales_data item2 = null_book; // 错误,拷贝初始化隐式转换已被阻止item.combine(null_book); // 错误,string的构造函数是explicititem.combine(Sales_data(null_book)); // 正确item.combine(static_cast<Sales_data>(null_book)); // 正确,static_cast是显示转换

13.类的静态成员

静态成员包括静态成员函数、静态数据成员

类的静态成员存在于任何对象之外,对象不包括任何与静态数据成员有关的数据。

静态成员函数也不包含this指针!!!,因此访问不到非静态成员

静态成员可以在类外或类内定义(初始化),但static声明只能在类内

class A {public:    static int i; // static成员只能在类内声明};int A::i = initRate(); // 可以在类外定义和初始化

static数据成员如果在类内初始化,必须要是常量表达式

  • 静态成员能用于默写场景,而普通成员不能
class Bar{public:    // ...private:    static int bkground;    static Bar mem1; // 正确,静态成员可以是不完全类型    Bar *mem2;  // 正确,指针成员可以是不完全类型    Bar mem3; // 错误,数据成员必须是完全类型public:    void clear(char = bkground); // 可以使用static成员作为默认实参};
原创粉丝点击