类--

来源:互联网 发布:逗游游戏盒mac版 编辑:程序博客网 时间:2024/04/29 21:02


1. this指针

class Sales_data{}Sales_data total;total.isbn();           //实际执行的是下面这个Sales_data::isbn(&total);

默认情况下,this的类型是指向类类型非常量版本的常量指针.this的类型是void *const this, 而对于像这种后面加const的,
std::string isbn() const{}
则this变成 const void *const this类型,故不可以改变调用者的数据成员


2.构造函数

  • 合成的默认构造函数

    • 如果我们的类没有显示地定义构造函数,那么编译器就会为我们隐式地定义一个默认构造函数.编译器创建的构造函数又被称为默认构造函数.只有当类没有声明任何构造函数时,编译器才会自动地生成默认构造函数.
  • =default的含义

    • Sales_data() =default;
      我们定义这个构造函数的目的仅仅是因为我们既需要其他形式的构造函数,也需要默认的构造函数.=default要求编译器生成构造函数.

注:构造函数不能被声明const的.当我们创建一个const对象时,直到构造函数完成初始化过程,对象才能真正取得其”常量”属性.因此,构造函数在const对象的构造过程中可以向其写值.


3 . 类的声明

 class Screen

这种声明有时被称作为前向声明.对于类型Screen来说,在它声明之后定义之前是一个不完全类型.也就是说我们已知Screen是一个类类型,但是不清楚它到底包含哪些成员.

不完全类型只能在非常有限的情景下使用:可以定义指向这种类型的指针或引用,也可以声明(但是不能定义)以不完全类型作为参数或者返回类型的函数.


4.类的作用域

  • 普通函数名字查找

    • 首先,在名字所在的块中寻找其声明语句,只考虑在名字的使用之前出现的声明.
    • 如果没找到,继续查找外层作用域.
    • 如果最终没有找到匹配的声明,则程序报错.
  • 类内部的成员函数名字查找

    • 首先,编译成员的声明.
    • 直到类全部可见后才编译函数体.
  • 成员定义中的普通块作用域的名字查找

    • 首先,在成员函数中查找该名字的声明.和前面一样,只有在函数使用之前出现的声明才被考虑.
    • 如果在成员函数内没有找到,则在类中继续查找,这时类的所有成员都可以被考虑.
    • 如果类内也没找到该名字的声明,在成员函数定义之前的作用域内继续查找.

因为成员函数体直到整个类可见后才会被处理,所以它能使用类中定义的任何名字.相反,如果函数的定义和成员的声明被同时处理,那么我们不得不在成员函数中只使用那些已经出现的名字.

一般来说,内层作用域可以重新定义外层作用域中的名字,即使该名字已经在内层作用域中使用过.而在类中,如果成员使用了外层作用域中欧给你的某个名字,而该名字代表一种类型,则类不能在之后重新定义该名字.


5.构造函数再探

  • 成员初始化的顺序
    • 构造函数的初始值只说明用于初始化成员的值,而不限定初始化的具体执行顺序.
    • 成员初始化顺序与它们在类定义中出现的顺序一致:第一个成员先被初始化,然后第二个,一次类推..构造函数初始值列表中的初始值的前后位置关系不会影响实际的初始化顺序.
  • 委托构造函数:
    • 一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程.
      当一个构造函数委托给另一个构造函数时,受委托的构造函数的初始值列表和函数体依次被执行.
  • 默认初始化在以下情况下执行:
    • 当我们在块作用域内不使用任何初始值定义一个非静态变量或则数组时.
    • 当一个类本身含有含有类类型的成员且使用合成的默认构造函数时.
    • 当类类型的成员没有在构造函数初始值列表中显示地初始化时.
  • 隐式的类类型转换
    定义:如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,我们称之为转换构造函数.
    • 只允许一步类类型转换
item.combine("9-999-99999-9"); //  错误item.combine(string("9-999-99999-9"))   //正确
  • Explicit
    • 关键字explicit只对一个实参的构造函数有效,需要多个实参的构造函数不能用于执行隐式转换,所以无须将这些构造函数指定为explicit的.只能在类内声明构造函数时使用explicit关键字,在类外部定义时不应重复.
  • 聚合类
    • 所有的成员都是public的.
    • 没有定义任何构造函数.
    • 没有类内初始值.
    • 没有基类,也没有virtual函数.
struct Data{    int ival;    string s;    };Data vall = {0,"Anna"};     //初始值的顺序必须与声明的顺序一致.
  • 字面值常量类
    • 数据成员都是字面值类型的聚合类是字面值常量类.如果一个类不是聚合类,但它符合下述要求,则它也是一个字面值常量类:
      • 数据成员都必须是字面值类型.
      • 类必须至少含有一个constexpr构造函数.
      • 如果一个数据成员含有类内初始值,则内置类型成员的初始值必须是一条常量表达式;或者如果成员属于某种类类型,则初始值必须使用成员自己的constexpr构造函数.
      • 类必须使用析构函数的默认定义,该成员负责销毁类的对象.

6.类的静态成员

  • 静态成员函数不与任何对象绑定在一起,它们不包含this指针.作为结果,静态成员函数不能声明成const的,而且我们也不能在static函数体内使用this指针.

  • 在类的外部定义静态成员时,不能重复static关键字,该关键字只能出现在类的内部的声明语句中.

  • 静态数据成员并不是在创建类的对象时被定义的.这意味着它们不是由类的构造函数初始化的.而且一般来说,我们不能在类的内部初始化静态成员.相反的,必须在类的外部定义和初始化每个静态成员.和其他对象一样,一个静态数据成员只能定义一次.且可以访问类的私有成员

静态数据成员可以是不完整类型.特别的,静态数据成员的类型可以就是它所属的类类型.而非静态数据成员则受到限制,只能声明成它所属类的指针或引用.

class Bar{public:    //.....private:    static Bar mem1;      //正确,静态成员可以是不完全类型    Bar *mem2;            //正确,指针成员可以是不完全类型    Bar mem3;             //错误,数据成员必须是完全类型 };

静态成员和普通成员的另一个区别是我们可以使用静态成员作为默认实参:

class Screen {public:     // bkground表示一个在类中稍后定义的静态成员     Screen& clear(char = bkground);private:     static const char bkground;};

非静态数据成员不能作为默认实参,因为它的值本身属于对象的一部分,那么做的结果是无法提供一个对象以便从中获取成员的值,最终引发错误.

0 0
原创粉丝点击