类 - 3【C++ Primer 学习笔记 - 第十二章】

来源:互联网 发布:asp编程入门 编辑:程序博客网 时间:2024/06/06 17:29

默认实参

class Sale_item{public:// 隐式使用string 的默认构造函数初始化 isbnSale_item():price(0.0){}Sale_item(const string &book):isbn(book), price(0.0){}// 建议使用默认实参,将上述2个构造函数合并。如下:// Sale_item(const string &book = ""):isbn(book), price(0.0){}private:string isbn;double price;};

合成的默认构造函数
如果,类没有定义构造函数,编译器将会自动生成一个默认的构造函数。
但是,如果已经定义过构造函数(哪怕只有1 个), 编译器就不会再生成默认构造函数。
理由:因为一个类,在某种情况下,需要控制对象的初始化,则,该类很可能在所有情况下都需要控制。

合成的默认构造函数,的初始化规则,与变量的初始化规则相同。
类类型,使用各自的默认构造函数,来初始化
内置、复合类型,如:指针、数组,只对定义在全局作用域中的对象才初始化,
定义局部作用域中,则,内置、复合类型不初始化,处于未定义状态。


类通常,都应该定义一个默认构造函数

假设:NoDefault ,是一个类,它具有接受一个 string 实参的构造函数,
这种情况下,编译器,不会生成默认构造函数。
于是,
1、当我定义一个类 A,具有 NoDefault 类型的成员,则,
A 的所有构造器,都必须通过传递一个初始 string 来初始化 NoDefault 类型的成员

2、当我定义一个类 A,具有 NoDefault 类型的成员,则,
编译器将不会生成 A 的默认构造器。只能自己显示定义

3、NoDefault 类型,不能用作动态分配数组的元素类型。

int *iArr = new int[10];// 上式可行,下式报错NoDefault *arr = new NoDefault[10];

4、NoDefault 类型的静态分配数组,也必须为每个元素提供显示的初始化
5、如果有保存 NoDefault 对象的容器,如:vector,
则,容器的构造函数,不仅要提供容器大小,也要提供元素初始化式。



// 这是一个函数声明,函数返回类型:Sales_itemSales_item myObj();// Sales_item myObj2;// 创建一个 Sales_item 对象,并用默认构造函数初始化Sales_item myObj3 = new Sales_item();


隐式类型转换

class Sales_item{public:Sales_item(const string &book = ""):isbn(book), units_sold(0), revenue(0.0){}// explicit 只能用于,类内部的构造函数声明上,在类的定义体外部则不用重复它explicit Sales_item(istream &is);Sales_item();bool same_isbn(Sales_item item){return item.isbn == isbn;}private:string isbn;int units_sold;double revenue;};// 错误: explicit 只能在类内部的构造函数声明上explicit Sales_item::Sales_item(istream &is){/* ... */}/* ... */string null_book = "9-999-9999-9";Sales_item item_a = Sales_item("1-111-1111-1");// 以下会隐式调用构造函数,生成一个 Sales_item 对象,来进行比较// 但这种隐式转换,未必是我们真正想要的。// 为阻止这种隐式转换,可以在构造函数前,使用 explicit 关键字item_a.same_isbn(null_book);// 建议使用下面的方式,避免错误item_a.same_isbn(Sales_item(null_book));


// 没有定义构造函数、并且,全体数据成员都是 public 的类,// 可以采用,与数组元素相同的方式,来初始化成员// 但,还是推荐,使用构造函数struct Data{int ival;char *ptr;};Data val1 = {0, 0};


友元,允许一个类,将自己的,非公有成员的访问,授权给,指定的类或者函数。
它只能出现在类定义的内部。通常,将所有的友元声明,成组地放到类定义的开始或者结尾。

class Screen{public:typedef string::size_type index;private:int height;int width;// 友元不是 Screen的成员,// 它可以出现在 Screen 类定义体中的,任何地方。// 并且,不受访问控制(private、public)的影响friend class Window_Mgr;// 将,其他类的成员函数,设置为友元friend Window_Mgr& Window_Mgr::relocate(Screen::index, Screen::index, Screen&);};class Window_Mgr{public:Window_Mgr& relocate(Screen::index r, Screen::index c, Screen &s){s.height += r;s.width += c;return *this;}private:};


要将类的成员函数,设为友元,则这个类必须先定义
要将类 或者 非成员函数 设为友元,则,不需要预先声明。
(P398,例子,貌似有误)

全局对象,会破坏封装性,而,类中定义的 静态成员,则能保持很好的封装性。
static 数据成员,与类相关联,而不是与类的对象关联。
static 成员函数,没有 this 形参。

class Account{public:void applyint(){ amount += amount * interestRate; }static double rate() { return interestRate; }static void rate(double);private:string owner;double amount;static double interestRate;static double initRate();// 例外,const 类型的静态数据成员,可以在类定义体中初始化// 但是,即使如此,也必须在外部,进行定义static const int period = 30;};// 外部定义,但此时,无须提供初始化式const int Account::period;// 内部已经声明为 static 了// 外部定义的时候,不需要再指定 static// static 不能声明为 const、也不能声明为 虚函数// 声明为 const 是承诺不修改该函数所属的对象,// 然而, static 函数,不属于任何对象,它只与类关联void Account::rate(double newRate){interestRate = newRate;}// static 数据成员,必须在类定义体的外部定义(刚好一次)// 一旦成员名出现,static 成员的定义,就在类作用域中了// 因此,可以直接使用 私有成员函数 initRatedouble Account::interestRate = initRate();Account ac1;Account *ac2 = &ac1;double rate;rate = ac1.rate();rate = ac2->rate();rate = Account::rate();

class Bar{public:private:// static 数据成员的类型,可以是该成员所属的类类型。static Bar mem1;Bar *mem2;// 错误Bar mem3;};class Screen{public:Screen& clear(char = bkground);private:// static 数据成员,可以作为默认实参。static const char bkground = '#';};