侯捷c++面向对象 笔记(一)

Class without pointer members

引用头文件

引用标准库头文件使用尖括号<>
引用自己写的头文件使用双引号””
c++中头文件引用可以省略.h

头文件防卫式声明

1
2
3
4
#ifndef COMPLEX_COMPLEX_H
#define COMPLEX_COMPLEX_H
...
#endif //COMPLEX_COMPLEX_H

​ 避免重复include,同一个程序中如果之前已经include之后,再次进来就不会走…的代码了

class的声明

有些函数可以直接在头文件的class body内定义,有些在body之外定义

内联函数inline

函数在class本体内定义完成,成为内联函数
内联函数优点,快;缺点,会复制到每个调用的地方,产生额外的内存消耗
函数太复杂无法inline
即使写在body中,也是由编译器决定是否为inline函数的,可以加inline关键字,但最终也是由编译器决定的

访问级别access level

私有的private:数据部分、部分函数
公开的public:部分函数
保护的protected:

构造函数constructor,ctor

函数名称与类名相同
可以传入参数,且可以有默认值,创建对象时没有指明构造函数入参,则使用默认值
没有返回类型,不需要有,构造函数就是用来创建对象的,返回的一定是这个对象
构造函数的特殊语法——初值列initialization list,尽量不要在{}内使用赋值的方式初始化,而是单独起一行对
下面是两种构造函数的写法,推荐第一种使用初值列的写法:

1
2
3
4
5
6
class complex {
public:
complex (double r = 0, double i = 0)
: re (r), im (i)
{}
};

类似普通函数的传统写法:

1
2
3
4
5
6
7
class complex {
public:
complex (double r = 0, double i = 0) {
re = r;
im = i;
}
};

原因是,给一个变量的赋值有两个阶段,一个是初始化,另一个是后面再赋值,构造函数内值的赋予如果放在大括号里面来做,相当于放弃了初值列这个初始化的阶段,时间点晚了一些,效率也会低一些。所以虽然结果一致,但是过程是不一样的
​不能在程序里面调用构造函数,只能通过创建对象
​也有对应的析构函数
​不带指针的类,多数情况不用析构函数

构造函数可以有很多个——重载overloading

函数重载常常发生在构造函数上
对于同名函数,到底调用哪一个的问题,由编译器决定
编译器会把函数名、参数数量、参数类型进行编码

1
2
3
4
5
6
7
//函数一
double real () const { return re;}
//函数二
void real(double r) { re = r;}
//real函数编译后的实际名称可能是
?real@Complex@@QBENXZ
?real@Complex@@QAENABN@Z

构造函数被放在private区

一般来说不会出现,否则无法构造对象
在单例模式中,会出现这种情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class A {
public:
static A& getInstance();
setup() {...}

private:
A();
A(const A& rhs);
...
};

A& A::getInstance()
{
static A a;
return a;
}

//使用:
A::getInstance().setup();

常量成员函数 const member functions

类的成员函数分为两种,会改变类对象数据的和不会改变数据的,对于不会改变数据内容的函数,请加上const
当创建对象前出现const,表示这个对象是不可修改的,这种情况下,如果real和imag函数这时候没加const会出现编译错误

1
2
3
4
5
6
7
8
9
//定义是没加const
double real () { return re;}

//创建时定义const对象
{
const complex c1(2,1);
cout << c1.real();
cout << c1.imag();
}

参数传递 pass by value vs. pass by reference(引用)(to const)

值传递:值多大就传多大
引用传递:相当于传指针,底部就是一个指针
如果传过去的引用不希望被更改,则加const

返回值 return by value vs. return by reference(引用)(to const)

传递者无需知道接受者是否以引用的形式接收

1
2
3
4
inline complex& __doapl (complex* ths, const complex& r) {
...
return *ths;
}

返回值为引用,实际return的时候却是具体的值

友元friend

朋友才可以来拿数据
比如数据re和im被封装在private,如果一个函数被声明为friend,则可以直接拿private中的数据
friend打破了封装,不用友元就需要通过额外的函数来拿,效率有损失

1
2
3
4
5
inline complex& __doapl (complex* ths, const complex& r) {
ths->re += r.re;
ths->im += r.im;//自由取得friend的private成员
return *ths;
}

​ 相同class的各个objects互为友元,不需要单独声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class complex {
public:
complex (double r = 0, double i = 0)
: re (r), im (i)
{}

int func(const complex& param) {
return param.re + param.im;
}
private:
double re, im;
}

{
complex c1(2,1);
complex c2;

c2.func(c1);
}

class body外的各种定义

考虑:什么情况下可以pass by reference?什么情况下可以return by reference?
一个函数的结果是否需要开辟新的空间来存放?如果是,则不能通过引用的方式返回出去;如果不是则可以

运算符重载1——成员函数(所有的成员函数都有一个隐式参数this指针,指向这个函数的调用者,不同的编译器放的位置不同,可能在第一个,可能在最后一个)

实际上在c++中,操作符本身就是一种函数,所以允许对操作符进行自定义——运算符重载

运算符重载2——非成员函数(无this)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//c2 = c1+c2
inline complex operator + (const complex& x, const complex& y) {
return complex (real(x) + real(y), imag(x)+ imag(y));
}

//c2 = c1+5
inline complex operator + (const complex& x, double y) {
return complex (real(x) + y, imag(x));
}

//c2 = 5+c1
inline complex operator + (double x, const complex& y) {
return complex (x + real(y) , imag(y));
}

这些函数绝对不能返回引用,因为+操作必定返回一个新的对象

小总结——创建一个类需要注意

  • 构造函数写法,尽量使用初始列
  • 成员函数声明能否加const,能加尽量加
  • 函数入参与返回值能用引用尽量使用引用
  • 入参是否要加const
  • 数据尽量放在private
  • 函数主要放在public
文章作者: ゴウサク
文章链接: http://dapaner.top/2022/04/10/侯捷c++面向对象 笔记(一)/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Corner&Coder
微信赞赏码
支付宝赞赏码