C++面向对象程序设计笔记:第一章 从C到C++
引用
定义引用时一定要将其初始化成引用某个变量。
初始化后,它就一直引用该变量,不会再引用别的变量了。
引用可以作为函数的返回值。
int n = 4;
int& SetValue() { return n; }
int main() {
SetValue() = 40;
cout << n;
return 0;
}// 输出:40
在引用前加const可以变为“常引用”:不能通过常引用去修改其引用的内容。
int n = 100;
const int& r = n;
r = 200; // 编译错误
n = 300; // ok
常引用和非常引用的转换:
const T& 和 T& 是不同的类型
T& 类型的引用或T类型的变量可以用来初始化const T& 类型的引用。
const T 类型的常变量和const T& 类型的引用则不能用来初始化T& 类型的引用,除非进行强制类型转换。
const关键字
定义常量指针:不可通过常量指针修改其指向的内容。
int n, m;
const int* p = &n;
*p = 5; // 编译出错
n = 4; // ok
p = &m; // ok,常量指针的指向可以变化
不能把常量指针赋值给非常量指针,反过来可以。
const int* p1; int* p2;
p1 = p2; // ok
p2 = p1; // 编译出错
p2 = (int*)p1; // ok,强制类型转换
函数参数为常量指针时,可避免函数内部不小心改变参数指针所指地方的内容
void MyPrintf(const char* p) {
strcpy(p, "this"); // 编译错误
printf("%s", p); // ok
}
动态内存分配
用法1:分配一个变量
P = new T;
T是任意类型名,P是类型为T*的指针。
动态分配出一片大小为sizeof(T)字节的内存空间,并且将该内存空间的起始地址赋值给P。
int *pn;
pn = new int;
*pn = 5;
用法2:分配一个数组
P = new T[N];
T: 任意类型名
P: 类型为T的指针
N: 要分配的数组元素的个数,可以使整数型表达式
动态分配出一片大小为Nsizeof(T)字节的存储空间,并且将该内存空间的起始地址赋值给P。
int* pn;
int i = 5;
pn = new int[i * 20];
pn[0] = 20;
pn[100] = 30; // 编译没问题,但运行时会产生越界
用delete运算符释放动态分配的内存
用new动态分配的内存空间,一定要用delete运算符进行释放
delete 指针; // 该指针必须指向new出来的空间
int* p = new int;
*p = 5;
delete p;
delete p; // 导致异常,一片空间不能被delete两次
用delete释放动态分配的数组时,要加”[]”
delete[] 指针; // 该指针必须指向new出来的数组
int* p = new int[20];
p[0] = 1;
delete[] p;
内联函数、函数重载、函数缺省
函数调用是有时间开销的。如果函数本身只有几条语句,执行非常快,而且函数被反复执行很多次,相比之下调用函数所产生的这个开销就会小显得比较大。
为了减少函数调用的开销,引入了内联函数机制。
编译器处理对内联函数的调用语句时,是将”整个函数的代码插入到调用语句处”,而不会产生调用函数的语句。
在函数定义前面加inline关键字,可定义内联函数。
函数重载:一个或多个函数,名字相同,然而参数个数或参数类型不相同。
编译器根据调用语句中实参的个数和类型判断应该调用哪个函数。
注意:如果只有返回参数不同,不能叫重载,会报错重定义。
(1) int Max(double f1, double f2) {}
(2) int Max(int n1, int n2){}
(3) int Max(int n1, int n2, int n3){}
Max(3.4, 2.5); // 调用(1)
Max(2, 4); // 调用(2)
Max(1, 2, 3); // 调用(3)
Max(3, 2.4); // 编译错误,二义性
函数的缺省参数:定义函数的时候可以让最右边的连续若干个参数有缺省值,那么调用函数的时候,若相应位置不写参数,参数就是缺省值。
void func(int x1, int x2=2, int x3=3) {}
func(10); // 等效于func(10,2,3)
func(10, 8);// 等效于func(10, 8, 3)
func(10, ,8); // 编译错误,只能最右边的连续若干个参数缺省
函数参数可缺省的目的在于提高程序的可扩充性。
即如果某个写好的函数要添加新的参数,而原先那些调用该函数的语句,未必需要使用新增的参数,那么为了避免对原先那些函数调用语句的修改,就可以使用缺省参数。
课后习题
001:简单的swap
#include <iostream>
using namespace std;
class A
{
public:
int x;
int getX() { return x; }
};
void swap(
A& a, A& b
)
{
int tmp = a.x;
a.x = b.x;
b.x = tmp;
}
int main()
{
A a,b;
a.x = 3;
b.x = 5;
swap(a,b);
cout << a.getX() << "," << b.getX();
return 0;
}
002:难一点的swap
#include <iostream>
using namespace std;
void swap(
int*& a, int*& b
)
{
int * tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 3,b = 5;
int * pa = & a;
int * pb = & b;
swap(pa,pb);
cout << *pa << "," << * pb;
return 0;
}
003:好怪异的返回值
#include <iostream>
using namespace std;
int&
getElement(int * a, int i)
{
return a[i];
}
int main()
{
int a[] = {1,2,3};
getElement(a,1) = 10;
cout << a[1] ;
return 0;
}
004:神秘的数组初始化
#include <iostream>
using namespace std;
int main()
{
int * a[] = {
NULL,
new int,
new int,
new int[6]{1, 1, 1, 1, 1, 1}
};
*a[2] = 123;
a[3][5] = 456;
if(! a[0] ) {
cout << * a[2] << "," << a[3][5];
}
return 0;
}