LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

C++面向对象程序设计笔记:第一章 从C到C++

2022/9/13 C++入门

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: 要分配的数组元素的个数,可以使整数型表达式
动态分配出一片大小为N
sizeof(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;
}
img_show