引言
类(class)的使用分为两种——基于对象(object Based)和面向对象(object oriented)
基于对象是指,程序设计中单一的类,和其他类没有任何关系
单一的类又分为:不带指针的类(class without pointer members)和带指针的类(class with pointer members)
面向对象则是类(class)中涉及了类之间的关系:复合(composition)、委托(delegation)、继承(inheritance)
1.头文件的防御式声明
- #ifndef xxx
- #define xxx
- ...
- #endif
复制代码 在编写头文件时应该有这样的一种习惯
目的是避免多次重复包含同一个头文件,否则会引起变量及类的重复定义
2.使用初始化列表的好处
- 只有构造函数这类函数具有“初始化列表”这一特性
- 从结果上来看,构造函数时使用初始化列表和在类内赋值是一样的,但我们都知道一个变量必须先初始化然后才被赋值,而初始化列表顾名思义是只执行初始化这一步,在类内赋值时就要先初始化再被赋值,所以从执行效率上讲,使用初始化列表会更快,也更简洁
3.设计模式:singleton(单例类)
- class A {
- public:
- static A & getInstance();
- setup() {...}
- private:
- A();
- A(const A & rhs);
- ...
- }
- A & A::getInstance()
- {
- static A a;
- return a;
- }
- ...
- //外部接口
- A::getInstance().setup();
复制代码
- 原理:将构造函数设置为私有属性,同时设置一个静态函数接口返回一个该类对象
- 作用:保证每一个类仅有一个实例,并为它提供一个全局访问点
单例模式(Singleton)的主要特点不是根据用户程序调用生成一个新的实例,而是控制某个类型的实例唯一性。它拥有一个私有构造函数,这确保用户无法通过new直接实例它。除此之外,该模式中包含一个静态私有成员变量instance与静态公有方法Instance()。Instance()方法负责检验并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。
这种模式主要有以下特征或条件:
- 有一个私有的无参构造函数,这可以防止其他类实例化它,而且单例类也不应该被继承,如果单例类允许继承那么每个子类都可以创建实例,这就违背了Singleton模式“唯一实例”的初衷。
- 单例类被定义为sealed,就像前面提到的该类不应该被继承,所以为了保险起见可以把该类定义成不允许派生,但没有要求一定要这样定义。
- 一个静态的变量用来保存单实例的引用。
- 一个公有的静态方法用来获取单实例的引用,如果实例为null即创建一个。
- 参考:
设计模式详解:Singleton(单例类)_singleton类_p_帽子戏法的博客-CSDN博客
单例模式(Singleton)的6种实现 - JK_Rush - 博客园 (cnblogs.com)
4.常成员函数的重要性
- 如果一个成员函数不改变类的数据成员时,就把它声明为常函数,这是一个好的习惯
- 当实例化一个常对象时,常对象要求不能改变数据成员,如果成员函数不加const,将无法调用此成员函数,编译器不会通过,即使此函数确实没有改变数据成员;同时,即使成员函数被声明为了常函数,实例化一个普通对象时依然可以调用。
- 简单来说,不声明为常成员函数可能不会有问题,但声明为常成员函数能确保一定不出问题
5.如何解释成员函数接收同类对象参数...
- 问:如何解释一个类的成员函数在接收同类对象的参数(比如拷贝构造函数)可以直接调用该对象的任何成员,明明既不是友元也不是嵌套?
- 答:相同class的各个objects互为friends(友元)
6.设计一个类要考虑什么
- 目的:高效、安全、简洁、严密
- 1.数据成员私有
- 2.参数传递和返回值优先考虑用引用(传递的是地址值,这样不管传递的数据内存占用多大,依然固定传入四个字节,即使当传递字符这样小于四个字节时用值传递确实比引用或指针传递更快一些,但不必考虑这些细枝末节)
- 3.构造函数优先去使用初始化列表
- 4.能声明为常成员就声明为常成员
7.返回值加不加引用
- 取决于返回的值是否要改变、是否可以改变,前者由我们决定,后者由语法限制
两个案例- class A
- {
- int value;
- ...
- };
- ...
-
- A& fun1(A* x, const A& y)
- {
- x.value += y.value; //第一参数会改变,第二参数不会改变
- return *x
- }
复制代码- class B
- {
- int value;
- ...
- };
- ...
- B fun2(const B& x, const B& y)
- {
- //第一参数和第二参数都不会改变
- return B(x.value + y.value);
- }
复制代码 8.运算符重载成员函数的思考
e.x.- inline complex&
- _doapl(comlex* ths, const complex& r)
- {
- ...
- return *ths;
- }
- inline complex&
- complex::operator += (const comlex& r)
- {
- return _doapl(this, r);
- }
- ...
- comlex c1(2,1), c2(3), c3;
- //c2 += c1;
- //c3 += c2 += c1;
复制代码 当重载一个二元运算符为成员函数时,我们知道重载函数除了右操作数是我们传递的,函数还会默认用一个this指针,来接收左操作数
那么可能会有疑问,我们想要改变的是左操作数,而且由于传递的是指针,函数内也确实可以改变,那返回值又有什么用呢,声明为空不就行了。
当我们使用重载运算符时只是像被注释的第一行代码一样,那么返回值确实不重要,但是当我们使用的形式像被注释的第二行代码时,那么返回值就很重要,因为c2.+=(c1) 这个函数的返回值就是 c3 += () 函数的参数
9.temp object(临时对象)
- 语法: typename ();
- 生存期:仅声明那一行
- 返回值是临时对象时不难return by reference
10. |