1.5 内置函数
- C语言中,提升效率的方法是使用宏定义:#define area(a,b) (a)*(b);
- C++中,提升效率的方法是使用内置函数:
1.6 有默认参数的函数
- 函数声明时为形参指定默认值,例
float (r=6.5);//给定r的默认参数是6.5
- 函数声明时,形参名可以不写,例
area(float, float = 4.2);
- 指定默认值的参数必须放在形参列表中的最右端,否则出错,例
void f1(float a, float b=3.0, float c, float d=12.1);//错误
- 带默认参数的函数仅在声明时设默认参数,而定义时不能设默认参数。
1.7函数重载
- 同名同体,但接口不同;
- 同命不同体,但参数个数不相同;(即重载函数除了参数的类型不一样,参数的个数也可以不一样。)
- 实现细节不同,同一类的功能时用重载函数
- 函数的返回值类型不同,但参数个数和类型相同,不是重载
- 重载函数的参数个数,参数类型,参数顺序必须至少有一个不同,返回值类型可以相同,也可以不同;
- 函数的重载不要与函数的默认值有冲突;
1.8函数模板
- 替代同体重载函数的多次调用;
- 先进行函数声明:
- template<typename T>
- 或template<class T>
- 类型参数可以不只一个,template<class T1, typename T2>(详见代码)
1.9 字符串类
1.10 C++编程环境
- 建议所存放的文件夹的名称中不要有空格和汉字;
- 用F10或F11进行调试,查找问题的地方;
2.1 初见对象
2.3 基于对象的程序的执行过程
- 关键字this:前加一个“*”(即*this)表示指向当前对象值的指针。
2.4 类的成员函数
- 如果一个类中不包含成员函数,就等同于C语言中的结构体了。体现不出类在面向对象程序设计中的作用。
- private只能被类内的成员函数调用,类外不能被访问;public,在类的外可以被自由访问;
- 将需要被外界调用的成员函数指定为public,它们是类的对外接口;有的函数只被本类中的成员函数所调用,以支持其他函数的操作,应将它们指定为private。
- 成员函数可以访问本类中的任何成员,包括私有的和共有的。
2.5 对象成员的引用方法
-
void set_data();//成员函数 int num;//数据成员
- 面向对象思维解决时:要首先分析设计哪些实体,把它定义成哪些类;要分出数据成员和成员函数,然后实现它的成员函数,最后在main函数里面创建对象,调用它的成员函数,对问题进行实现。
2.7 类的封装与信息的隐藏
- 函数外部:关心函数如何使用——函数的参数个数及各自的类型,函数的返回值类型,函数名,函数功能
- 函数外部:关心函数如何实现——采用什么计算方法,采用什么程序结构,怎样得到数据结果,性能如何保证...
- 一般对类来讲,将大多数的数据成员设置为私有的;大多数成员函数是私有的。特例,只被自己类内成员函数所调用的成员函数也被设置为私有的。
2.8 类声明和成员函数定义的分离
- 类声明放在头文件中;类外的函数定义放在源文件中
-
一般情况下,自己定义的头文件放在和.cpp同样的文件夹下面#include <iostream>//"<>"用的时候会在系统文件中找 #include "student.h"//""""的话,优先去找当前目录
3.1构造函数
- structure 与class的区别:structure中的不指定访问权限时,默认是public的;class中不指定访问权限时,默认是private的。
- 构造函数不具任何数据类型(例如,time前无void等数据类型),不返回任何值。
- 构造函数的功能由用户定义,用户根据初始化的要求设计函数体和函数参数。
- 在实际应用中,在构造函数中,对数据成员初始化时,经常用到参数初始化表
class time { public: time() : hour(0), minute(0), second(0) {};//()中无参数,time是一个无参函数,{}中为空,说明函数体为空 time(int h, int m, int s) :hour(h), minute(m), second(s) {};//time()有三个参数,:后为参数初始化 ... }
- 类外用参数初始化表对数据成员初始化
不是在函数体内(也就是花括号内)对数据成员进行初始化,而是在函数的首部。time::time(int h, int m, int s) :hour(h), minute(m), second(s) {};
- 一般不在构造函数中加入与初始化无关的东西,以保证程序的清晰性;
- 在成员函数中,慎用输入输出,尤其是在图形用户界面中。
- name(s)和strcpy(name, s)都不能出现在首部中用来定义,如果非要这样,需要把char name[20];改成string name;
- 构造函数重载:同样一个函数,由于参数类型或参数个数不一样,是可以进行重载的。调用时,根据需要可以进行相应的重载。
3.2 默认构造函数(default constructor)
- 在调用时不必给出实参的构造函数
- 调用默认构造函数的格式
int main() { time t1;//不是t1() t1.show_time(); return 0; }
- 即使提供了其他的构造函数,提供一个默认构造函数总是对的。通常在默认构造函数中,给成员提供的初始值应该指出该对象是“空”的。
3.3带默认参数的构造函数
3.4析构函数
- 执行析构函数时,后产生的对象是先被析构的,而先产生的对象后被析构。
- 析构函数不返回任何值,没有返回类型,也没有函数参数
- 构造函数中,使用new运算符为对象成员动态地分配内存空间;析构函数中使用delete运算符释放分配的空间。
- 析构函数不能被重载。一个类中,构造函数可以有多个,但是析构函数只能有一个。
- 析构函数地作业不仅仅局限于释放资源方面。也可以用来进行“用户希望在最后一次使用对象之后后所执行的任何操作。”
- 一般情况下,类的设计者应该定义析构函数,来指定如何完成“清理”工作。如果用户没有定义析构函数,C++系统会在编译时自动生成一个析构函数。
3.5 调用构造函数和析构函数的顺序
3.6对象数组
- 结构体是可以定义数组的
struct A { int num; string name; char gender; }; A a[200];
- 对象和结构体是除了它访问权限不一样,其实都是一样的。更加喜欢用class描述这样的事情。
- 对象数组中,每一个元素都是同类中的对象
class A { public: A() {}; A(int num, string name, char gender) :n(num),a(name),g(gender){}//n, a, g 是数据成员 private: int n; string a; char g; }
- 对象数组中,每一个元素都是同类中的对象
- 定义类的时候可以是单一的一个的对象
对象在定义的时候可以进行初始化,依靠的是上面的构造函数A c;
对象也可以组成数组,有2个元素,每个元素是A类的对象。对象数组里的对象是数组,里面保存的是对象。计算机内存分配2个保存A对象的空间,并且这些空间是连续的。A d(4, "Rose", 'f');
定义对象数组时,同样可以用花括号括起的进行初始化。A e[2];
A a[2] = { A(1,"Jack",'m'), A(2,"Sally",'f') };
3.7对象指针
- 指向对象的指针——可通过对象指针访问对象和对象的成员
(*pt ).hour或pt->hour//即t1.hour
-
指向数据成员的指针——
int *p1;//定义指向数据成员的指针变量 p1 = &t1.hour; cout << *p1 << endl;//通过指向数据成员的指针变量访问数据成员
-
指向对象成员函数的指针——对于普通函数,占用内存,在程序区不在内存区,名字是首地址;
-
int(*p1)();//后面的括号表示p1是一个函数,括号是空的表示指向无参函数的指针。int表示它的返回值是一个整形。
-
void (Time:: *p2)();//定义指向类成员函数get_time()的指针
-
p2 = &Time::get_time;//指向一个成员函数,后面没有圆括号
&Time::get_time;//指向一个成员函数,后面没有圆括号 -
(t.*p2)();//t对象的指向函数的指针,而指向函数的指针是t。
t.*p2)();//t对象的指向函数的指针,而指向函数的指针是t。
3.8this指针
- 每个对象都可以利用一个自己的特殊指针,this——指向当前对象的指针。
-
this->x//x指的是数据成员 this->y//y指的是数据成员 x//指的是形式参数 y//指的是形式参数
3.9用const实施保护
- 常对象中所有对象的值都不能被修改;
- 常对象中两种等价定义形式——const 类名 对象名(实参列表);类名 const 对象名(实参列表);
- 常对象必须要有初值——const Test t1 (10,12,11); Test const t1 (10,12,11);
- 要引用常对象中的数据成员,需将该成员函数声明为const型函数(常成员函数),定义时也需要末尾加上const。
- 只能通过构造函数的参数初始化表对常数据成员进行初始化
Time(int h).hour(int h){}//在类内进行定义 Time::Time(int h).hour(int h){}//在类外进行定义
- 不能在构造函数中用赋值的方法对常数据成员进行初始化
Time::Time(int h)(hour = int h; }//错误
-
不能用成员函数改变常数据成员的值
void Time::set_time(int h) { hour = h ;}
- 如果一个对象被声明成常对象,那么将不能调用该对象的非const型的成员函数
const Time t1(10, 20, 30); t1.set_time();//非法
-
用常成员函数引用常变量
3.10 常指针和常引用
- 一个变量的引用就是变量的别名;变量名和内存名都指向同一段内存单元
int a;//定义a是整型变量 int &b=a;//声明b是a的引用 int a=20; cout<<b<<endl;
- 函数的形式参数可以是对象的引用
void fun(Time &t1);
-
如果不希望在函数中修改实参的值,可以将形参声明为常引用。
void fun(Time &);//引用的变量名在声明时可以不写
void doSomething(const Test &r){ r.setX(5);//非法,t1.setX(5)可以 r.printXY(); } int main(void){ Test t1(3,5); doSomething(t1); }
-
提倡用常引用作为函数参数,好处如下:能保证数据安全,使数据不能被随意修改;调用函数时不必建立实参的拷贝,这样可以提高程序运行效率。
3.13 对象的动态建立和释放 - 用new运算符动态地分配内存后,将返回一个指向新对象的指针的值,用户通过这个地址来访问对象。
Time *pt1=new Time; //Time对象初始化,调用默认构造函数,初始化的值为(0,0,0) Time *pt2; //定义另外一个指针变量 pt2=new Time(10,20,30);//执行有参数的构造函数 pt2->show_time();
- 建立的对象只能通过指针访问;建立对象时执行构造函数;内存不足,出现异常;
-
不需要由new运算符建立的对象时,用delete运算符释放。
-
建立指针数组:建立空间小的指针数组,灵活处理占用空间大的对象集合;指针数组保存的是指向对象的数组;
Sample temp[], *pTemp[];//第一个是对象数组,第二个是指针数组;对象数组给每一个对象分配空间,此时会对每个元素执行构造函数,而指针数组也会分配两个元素的空间,空间保存的是指向对象的指针,而对象目前并没有产生,只有以后用new产生才可以。
3.14 对象的赋值和复制
- 通过复制构造函数进行初始化的情况:
-
- 新建立一个对象:利用复制构造函数进行初始化:
Box box2(box1);
- 当函数的参数为类的对象:调用函数时
void fun(Box b) {} int main(){ Box box1(15,14,12); fun(box1); return 0; }
,将实参对象完整的传递给形参,通过调用复制构造函数来建立一个实参的拷贝; - 函数的返回值是类的对象:在函数调用完毕,将函数中的对象复制一个临时对象并传给该函数的调用处。
Box f() { Box box1(10,20,20); return box1; } int main() { Box box2; box2=f(); }
- 新建立一个对象:利用复制构造函数进行初始化:
3.15 深复制
- char * 专门用于指结尾是“/0”的字符串;
- 当一个类中有指针数据成员时,必须对该类里所有对象初始化,必须对该类里面所有对象的初始化做深复制。深复制的技术就是先给指针所指的变量分配空间,然后再去做其他的操作。
3.17 类模板
在类外定义成员函数
template <class numtype> //template <class 虚拟类型参数>
numtype Compare<numtype>::max() {//函数类型 类模板名 <虚拟类型> :: 函数(函数形参列表)
return (a < b) ? b : a;
}
3.20 函数中的引用
- 函数中的引用:引用作为形参;引用作为返回值。
- 当形式参数不是引用的形式时,形式参数向实际参数复制时,同样执行复制构造函数;当形式参数是引用的形式时,不需要实际参数的拷贝,不会产生新的对象,而是直接对实际参数的引用。
- 返回值为非引用对象时,不会执行复制构造函数,返回值直接取栈中结果。
- 函数的返回值是类的对象,函数在执行返回调用时,不会调用复制构造函数。(编译器自动进行优化的手段)
- 返回值为引用对象时,
-
在某一个地方的引用所对应的空间是有效的空间。
4.1 什么是运算符重载
- 重载:将同一个名字赋予新的含义。
- 函数的重载:对于一个函数赋予新的含义,使之实现新的功能。