Chapter7:类

  • 关于this指针

成员函数通过一个名为this的额外的隐式参数来访问调用它的对象。当我们调用一个成员函数时,用请求该函数的对象初始化this。

1 total.isbn();
2 //等价于编译器重写为
3 Sale_data::isbn(&total);

因为this的目的总是指向“这个”对象,所以this是一个常量指针,我们不允许改变this中保存的地址。

例如this的类型是Sale_data* const。但是this需要遵守初始化规则,所以我们不能把this绑定到一个常量对象。也即:我们不能在一个常量对象上调用普通的成员函数。

为了解决这一问题,引入了常量成员函数,即在成员函数之后加const。

常量对象、常量对象的引用或指针都只能调用常量成员函数。

  • 类作用域

类作用域的特点是:编译器分两步来处理:首先编译成员的声明,然后才轮到成员函数体。因此,成员函数体可以随意使用类中的其他成员而无须在意这些成员出现的次序。

类型名要特殊处理。在类中,如果成员使用了外层作用域中的某个名字,而该名字代表一种类型,则类不能在之后重新定义该名字。因此:类型名定义应该出现在类的开始处。

 1 typedef double Money;
 2 class Account
 3 {
 4 public:
 5     Money balance() { return bal; }
 6 private:
 7     typedef double Money;  //错误:不能重新定义Money
 8     Money bal;
 9 };
10
11
12 typedef double Money;
13 class Account
14 {
15     typedef double Money;  //可以
16 public:
17     Money balance() { return bal; }
18 private:
19     Money bal;
20 };

类的作者常常需要定义一些辅助函数,比如add、read、print。尽管这些函数定义的操作从概念上来说属于类的接口组成部分,但它们实际上并不属于类的本身。(因为类本身的话,会附加this参数)

1 istream &read(istream &is, Sale_data &item)
2 {
3     ;
4 }
5 ostream &print(ostream &os, const Sale_data &item)
6 {
7     ;//print函数不负责换行,一般来说执行输出任务的函数应该尽量减少对格式的控制,由用户代码确定是否换行。
8 }
  • 构造函数

构造函数不能声明为const,当我们创建类的一个const对象时,直到构造函数完成初始化过程,对象才能真正取得“常量”属性。

如果我们的类没有显式地定义构造函数,那么编译器就会为我们隐式地定义一个默认构造函数。

合成的默认构造函数:1)如果存在类内的初始值,用它来初始化成员;2)否则,默认初始化该成员。

对于一个普通的类来说,必须定义它自己的默认构造函数,原因有三

1)编译器只有在发现类不包含任何构造函数的情况下才会替我们生成一个默认构造函数;

2)合成的默认构造函数可能执行错误的操作。回忆之前:在块中定义的内置类型或复合类型的对象被默认初始化,则它们的值将是未定义的

3)有时候编译器不能为某些类合成默认的构造函数。

=default

我们定义此构造函数的目的仅仅是因为我们既需要其他形式的构造函数,也需要默认的构造函数。

构造函数初始化列表

当某个数据成员被构造函数初始化列表忽略时,它将以与合成默认构造函数相同的方式隐式初始化(在函数体执行之前)。

成员初始化的顺序与它们在类定义中的顺序一致。所以,Best Practice:初始值列表最好与成员声明顺序一致。

C++11委托构造函数

  • 类类型转换

我们可以为类定义隐式转换规则。如果构造函数只接受一个实参,则它实际上定义了次类类型的隐式转换机制。切记:只允许一步类类型转换。

使用explicit抑制隐式转换:

explicit只能在类内声明构造函数时使用,在类外部定义时不应该重复。

explicit只能用于直接初始化,不能用于拷贝初始化。

1 Sale_data item1(null_data);//正确
2 Sale_data item2=null_data;//错误

explicit可以显式转换

1 item.combine(Sale_data(null_book));
2 item.combine(static_cast<Sale_data>(cin))
  • 聚合类:可以使用花括号成员初始值列表

所以成员都是public

没有定义构造函数

没有类内初始值

没有基类,没有virtual

  • 字面值常量类:

数据类型都是字面值类型的聚合类是字面值常量类,或者符合下述条件的类:

1)数据类型都是字面值类型;

2)至少有一个constexpr构造函数;

3)数据成员:内置类型的初始值必须是常量表达式;类类型初始值必须使用constexpr构造函数;

4)类必须使用默认的析构函数

  • 类的静态成员

静态成员函数不与任何对象绑定在一起,即:不能使用this指针,不能声明为const。

当在类外部定义静态成员时,不能重复static关键字;因为不属于对象,所以不是由构造函数中初始化:需要在类的外部定义和初始化每个静态成员。

1 class Account
2 {
3     static double interestRate;
4 };
5 double Account::interestRate = 0;

当静态成员是const的时候,可以在类内提供初始值。此时,也应该在类外定义一下该成员。

1 class Account
2 {
3     static constexpr double interestRate = 0.8;
4 };
5 constexpr double Account::interestRate;

静态成员可以是不完全类型;可以做默认实参。非静态成员不能做默认实参,因为它值本身是对象的一部分,所以无法真正提供一个对象以便从中获取成员的值。

  • 友元

如果类想把一个函数作为它的友元,只需要增加一条以friend关键字开始的函数声明即可。

友元的声明仅仅是指定了访问权限,而非一个通常意义上的函数声明。如果希望调用某个友元函数,那么必须在友元声明之外再专门对函数进行一次声明。

(许多编译器并未强制限定友元函数必须在使用之前在类的外部声明)

令成员函数为友元

需要仔细组织程序结构以满足声明和定义的彼此依赖关系。

 1 class Window_mgr
 2 {
 3 public:
 4     void clear();
 5
 6 };
 7 class Screen
 8 {
 9     //不同于普通函数,之前必须声明
10     friend void Window_mgr::clear();
11 };
12
13 //Screen定义后,再定义clear函数,因为要用到Screen
14 void Window_mgr::clear()
15 {
16     Screen s;
17 }

类和非成员函数的声明不是必须在它们的友元声明之前。当一个名字第一次出现在一个友元声明中时,我们隐式地假定该名字在当前作用域中可见。

 1 class X
 2 {
 3 public:
 4     friend void f() {/*定义f*/; }
 5     X() { f(); }//错误,f还没有声明
 6     void g();
 7 };
 8
 9 void X::g() { f(); }//错误,f还没有声明
10 void f();//声明那个定义在X中的函数
11 void X::g() { f(); }//正确

类的声明

在声明之后定义之前,类是一个不完全类型。只能在有限情况下使用:

1)可以定义指向这种类型的引用或指针;2)可以声明(但不能定义)以不完全类型作为参数或者返回类型的函数。

时间: 2024-10-12 20:16:56

Chapter7:类的相关文章

(转)JDBC模板类。

Spring JDBC抽象框架core包提供了JDBC模板类,其中JdbcTemplate是core包的核心类,所以其他模板类都是基于它封装完成的,JDBC模板类是第一种工作模式. JdbcTemplate类通过模板设计模式帮助我们消除了冗长的代码,只做需要做的事情(即可变部分),并且帮我们做哪些固定部分,如连接的创建及关闭. JdbcTemplate类对可变部分采用回调接口方式实现,如ConnectionCallback通过回调接口返回给用户一个连接,从而可以使用该连 接做任何事情.State

python cookbook第三版学习笔记十二:类和对象(三)创建新的类或实例属性

先介绍几个类中的应用__getattr__,__setattr__,__get__,__set__,__getattribute__,. __getattr__:当在类中找不到attribute的时候,会调用__getattr__,并执行其中的自定义代码.所有在类中定义的属性都包含在__dict__中,也就是说如果在__dict__中找不到对应的属性名,则__getattr__被触发. class get_try(object):     def __init__(self,value):   

String类,主要还是把常用的几种方法都记住,加强练习

package chapter7; /** * String类是不可变类,即一旦一个String对象被创建以后,包含在里面的序列是不可变的. * StringBuffer对象则代表一个字符序列可变的字符串.最后也可以用toString()方法将其转换成String * StringBuilder功能和StringBuffer是一样的,只是它没有实现线性安全的,所以性能高. * String concat(String str):连接字符串 * boolean contentEquals(Stri

开涛spring3(7.2) - 对JDBC的支持 之 7.2 JDBC模板类

7.2  JDBC模板类 7.2.1  概述 Spring JDBC抽象框架core包提供了JDBC模板类,其中JdbcTemplate是core包的核心类,所以其他模板类都是基于它封装完成的,JDBC模板类是第一种工作模式. JdbcTemplate类通过模板设计模式帮助我们消除了冗长的代码,只做需要做的事情(即可变部分),并且帮我们做哪些固定部分,如连接的创建及关闭. JdbcTemplate类对可变部分采用回调接口方式实现,如ConnectionCallback通过回调接口返回给用户一个连

jvm系列(一):java类的加载机制

java类的加载机制 原文:http://www.cnblogs.com/ityouknow/p/5603287.html 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构.类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口. 类加载器并不需要等到某个

iOS -- SKSpriteNode类

SKSpriteNode类 继承自 SKNode:UIResponder:NSObject 符合 NSCoding(SKNode)NSCopying(SKNode)NSObject(NSObject) 框架  /System/Library/Frameworks/SpriteKit.framework 可用性 可用于iOS 7.0或者更晚的版本 声明于 SKSpriteNode.h 参考指南 Sprite Kit Progamming Guide 概览 重要提示:这是一个初步的API或者开发技术

iOS -- SKScene类

SKScene类 继承自 SKEffectNode:SKNode:UIResponder:NSObject 符合 NSCoding(SKNode)NSCopying(SKNode)NSObject(NSObject) 框架  /System/Library/Frameworks/SpriteKit.framework 可用性 可用于iOS 7.0或者更晚的版本 声明于 SKScene.h 参考指南 Sprite Kit Progamming Guide 概览 重要提示:这是一个初步的API或者开

iOS -- SKPhysicsWorld类

SKPhysicsWorld类 继承自 NSObject 符合 NSCodingNSObject(NSObject) 框架  /System/Library/Frameworks/SpriteKit.framework 可用性 可用于iOS 7.0或者更晚的版本 声明于 SKPhysicsWorld.h 参考指南 Sprite Kit Progamming Guide 概览 重要提示:这是一个初步的API或者开发技术文档.虽然已经审阅了本文档的技术准确性,但是它不是最终的版本.本机密信息仅适用于

C#嵌套类

嵌套类顾名思义就是类或者结构中定义的类 class Container { class Nested { Nested() { } } } <1>嵌套类的默认访问权限是private ,可以指定为public,protected,private,internal,protected internal.<2>嵌套类型可以访问外部类(包裹嵌套类的类),如果要访问外部类型,要把外部类通过构造函数传进一个实例<3>嵌套类中只能访问外部类中的静态成员,不能直接访问外部类的非静态成