目录 Table of Contents
3 构造函数
上一节我们讲了头文件的声明, 然后带了一下模板的概念
模板的概念我们后面会讲, 现在我们先回到没有模板的代码
inline (内联) 函数
什么情况下会形成 inline 呢, 你的函数在你的 class 本体里面定义, 就形成这种 inline, 在外面定义就不是 inline
inline 函数有什么好处, 它有宏的优点, 没有宏的缺点
当然, 如果你的函数太复杂, 编译器也没法把它 inline, 具体由编译器决定
换句话说, 函数如果在 class body 内定义完成, 它便自动成为了 inline 候选人
class complex
{
public:
complex (double r = 0, double i = 0)
: re (r), im (i)
{}
complex& operator += (const complex&);
double real() const
{
return re;
}
double imag() const {return im;}
private:
double re, im;
friend complex& __doapl (complex*, const complex&);
};
inline imag(const complex& x)
{
return x.imag();
}
告诉编译器, 让编译器尽量帮我 inline
access level 访问级别
类的访问控制
数据部分应该放到 private 里面, 因为我们希望数据对外隐藏, 只有自己人才看得到
大部分函数是要给外界使用, 所以放在 public 里面
不打算给外部调用的函数就放到私有里面
其实有第三中叫作 protected, 暂时不说它
constructor ctor 构造函数
当你创建一个对象的时候, 有一个函数会自动调用起来, 这个叫构造函数
class complex
{
public:
complex (double r = 0, double i = 0) // default argument (默认实参)
: re (r), im (i) // initialization list (初值列, 初始列)
{}
complex& operator += (const complex&);
double real() const
{
return re;
}
double imag() const {return im;}
private:
double re, im;
friend complex& __doapl (complex*, const complex&);
};
{
complex c1(2, 1);
complex c2; // 创建一个对象没有参数
complex* p = new complex(4); // 用动态方式创建一个复数, 得到的会是一个指针
...
}
构造函数名称一定要和类的名称一样
第二点, 它可以拥有参数, 对于复数我们自然会想到有两个参数-实部和虚部
上面代码中的默认实参, 就是说如果你创建对象的时候如果没有传参数的话, 就去用这个默认的
当然你指明了参数, 就用你指明的
除了构造函数之外, 其它的函数也可以用这种默认实参
构造函数没有返回值类型, 不需要有, 因为构造函数就是要来创建对象, 所以 C++ 告诉你不用写返回值
complex (double r = 0, double i = 0) : re (r), im (i) {}
一般我们函数收到参数之后, 可能会在大括号里面用赋值的操作来写, 这样写当然可以
但是构造函数有一种它特有的写法, 这一行就是 initialization list (初值列, 初始列)
它的意思是把 r 设置到 re 里面去, 把 i 设置到 im 里面去
其实你在大括号里面, 函数体里面, 用赋值去做, 也是一样的意思, 但是这里这样写会充分运用构造函数的特殊性
这里简单讲一下就是, 一个变量数值的设定有两个阶段, 一个是初始化, 一个是后面赋值; 第一个阶段就是初始化, 就是这里初始化初值列, 第二阶段就是大括号里面赋值
如果你直接在大括号里面写, 虽然结果是一样, 但是过程是不一样的
初始列这里语法很清楚, 有个冒号, 冒号后面能做哪些事情呢, 后面复杂的例子后面再说, 最常见的例子就是这里设置初始值
你不可能在你的程序里直接调用构造函数, 没有这种语法
对应的还有一种叫作析构函数, 这个以后再说
前面我们说过, 一个类有带指针的和不带指针的, 这里就是不带指针, 不带指针的多半不用写析构函数
ctor 构造函数可以有很多个 - overloading (重载)
class complex
{
public:
complex (double r = 0, double i = 0)
: re (r), im (i)
{}
complex() : re(0), im(0) {} // ?!
complex& operator += (const complex&);
double real() const
{
return re;
}
double imag() const {return im;}
private:
double re, im;
friend complex& __doapl (complex*, const complex&);
};
重载就是说可以有多个同名的函数 (在编译器看来它们其实不重名)
void real(double r) { re = r; }
比如我现在有个函数 real(), 它的作用是取得实部, 那么我们能不能再写一个函数 real() 它的动作是给实部赋值 ?
标题说函数可以重载, 所以是可以
这里我把 const
去掉, 我们这里暂时不解释 const 的意思, 如果你要加这个 real 函数的话必须要把这个 const 去掉
为什么可以有同名的函数, 将来调用的时候会调用哪个函数呢 ?
其实你写的这个 real, 编译之后可能不叫作 real, 编译器可能会把它写成类似这种形式 :
?real@Complex@@QBENXZ
?real@Complex@@QAENABN@Z
这个取决于编译器
刚刚说构造函数可以重载, 可以有好几个构造函数
那么我们看看新加上去的这一行代码
complex() : re(0), im(0) {}
这样写可以吗 ?
不可以
我们看这一段, 这两行代码都是创建对象
{
complex c1;
complex c2();
}
这里问题来了, c1 c2 都没有给参数, 编译器就不知道调用哪一个构造函数了
除了这种情况之外, 你还是可以写其它的构造函数