继续,泛型函数开始
- 作为一个好习惯,我们在头文件的代码中使用经过限定的名字,但是我们将假定对应的源文件中包含适当的using声明
- 当我们调用一个成员函数时,无法通过参数列表来说明这个对象是const类型。因此,我们只能限定函数本身,使之成为一个const成员函数。const成员函数无法改变他们所操作的对象的内部状态(无法修改数据成员)。
double grade(const Student_info&) {}; //原始的函数 double Student_info::grade() const; //作为成员函数
- 不能使用const对象调用非const函数,但是反过来可以。当把一个非const对象传递给一个带const引用参数的函数时,这个函数就会把这个对象当作是const对象来处理,编译器也将只允许他它调用这种对象的const成员。
- class是默认保护机制,不写public的会被默认定义为private
- 创建一个valid函数,检测对象是否为空
bool valid() const {return !homework.empty();} //homework为成员变量
- 我们无法显式的调用一个构造函数,但是创建类的一个对象的时候会自动的调用适当的构造函数
- 如果一个对象类型是没有定义任何构造函数的类,对这个对象进行值初始化或者默认初始化就会对他的每个成员变量进行适当的值初始化或者默认初始化。值初始化与默认初始化的区别
- 我们可以定义多个版本的构造函数,这些版本在参数的类型和数目上会有所不同。
- 不带任何参数的构造函数会被认为是默认构造函数
- 当我们创建一个新的类对象时
- 系统会分配内存空间,以保存这个对象
- 根据构造函数的初始化列表,初始化这个对象
- 执行构造函数的函数体
- 系统会初始化每个对象的每个数据成员,不管构造函数初始化列表中是否有这个成员。虽然构造函数的函数体随后可能会改变这些初始值,但是初始化列表会在构造函数的的函数体执行前进行初始化。通常来说,显式的为一个成员提供一个初始值,比在构造函数的函数体中对值进行初始化好的多。通过这样的初始化,而不是进行赋值操作,我们可以避免做两次相同的工作。
- 一个好的编程习惯是:对每个构造函数来说,再从构造函数退出前,我们应该确保每个数据成员都有一个有意义的值
- 成员初始化的顺序取决于他们在类中声明的顺序,所以用类的一个数据成员来初始化另一个成员的时候要小心。系那个避免这种依赖性的话,有一个比较安全的做法是,在构造函数的函数体中对这些成员进行赋值。
- 指针类型的局部变量在没有赋值之前没有任何有意义的值,可以使用0值来初始化指针(常量0是唯一一个可以转换为指针类型的整型值)。
- 推荐使用int* p,但是会隐藏一个重要的缺陷int* p,q;其实是int (*p),q;
- 由于对于一个函数来说,我们能做的仅仅是取得他的地址或者调用他而已,所以任何使用这个函数的地方,如果不是在调用这个函数,我们就认为是在取他的地址,即使没有显式的&
- 我们可以使用int (*fp)(int) 来生成一个函数指针。
int(*fp)(int); int next(int n) { return n + 1; } //以下两种调用方式等价 fp = next; fp = &next; fp = Mnext; fp = &Mnext; //可以通过fp来调用函数 int i = 55; i = fp(i); i = (*fp)(i);
- size_t 是数组的size_type,用来表示数组的长度,保存在<cstddef>中
- 数组名就是一个指向数组中首元素的指针,*coords 5; 就是将coords数组的第一个元素赋值为5
- 如果p和q都是指向同一个数组中元素的指针,那么p-q的结果是一个整数。它的类型的ptrdiff_t,也保存在<cstddef>中
- 将数组复制到vector中
vector<double> v; double coords[3]; copy(coords, coords + 3, back_inserter(v));
- 也可以使用迭代器来构造一个vector
vector<double> vv(coords, coords + 3);
- a[n]等价于*(a+n)
- 指向数组最后一个元素的后一个元素的地址,也是有意义的的(迭代器来索引?)
- strlen可以计算出数组中的字符数,但是不包括空字符。
- 显式化的初始数组可以避免显式的说明数组的大小
- 字符串直接量的实际含义:一个const char数组,它包含的元素个数比字面上的字符数多1。这个额外的字符是空字符'\0',编译器会自动的把这个字符添加到其他字符的后面。
- 因此,可以如下方式将const char[]转换为string
const char hello[] = { 'H', 'e', 'l', 'l', 'o', '\.' }; string s(hello);
- 我们也可以通过两个迭代器来构造一个string
string s(hello, hello + strlen(hello));
- 局部变量被声明为static,它的生存空间就可以不受函数的限制,因此我们每次在调用这个对象时,都是第一次调用时创建的。
static const char* const p[]; //是一个常量指针数组
- const char*, char const*, char*const的区别
- char *a 与char a[] 的区别
- sizeof(e)返回的是一个size_t类型的值,表示每个类型为e的对象占据多少个字节,sizeof无须对表达式求值,因为他并不需要对表达式求值来确定他的类型,也因为所有给定的类型的对象都占据相同大小的内存。(单位为字节)
- 求数组中元素个数的一个方法
sizeof(numbers) / sizeof(*numbers)
- C++程序不允许包含两个或两个以上的连续问号(?)
- clog流用在日志上(与cout流有相同的缓冲性质),cerr流总是立即进行输出。
- new T; 可为类型T的一个对象分配内存,而且这个对象是默认初始化的,然后这个表达式会生成一个指向这个新对象的指针。这个对象会会一直存在,直到程序结束或执行了delete(p就成了一个无效的指针)
- new T(args);用args来初始化对象,并返回一个指向这个对象的指针,例:int *p = new int(32);
- new T[n]会为包含n个T类型的对象的数组分配空间,并且返回一个指针(类型为T*),指向这个数组的首元素。数组的每个对象都会被初始化,也就是说,如果T是一个内置类型,这个数组是在局部空间分配的内存,那么这个对象就不会被初始化。如果T是一个类,那么每个元素都会通过这个累的默认构造函数来初始化。
- 当T为一个类时,数组中每个元素都会被初始化,这将会带来运行时的一定开销。
- delete[] p;表示的释放整个数组的内存,而不是一个元素。在释放这个数组之前,系统会按照逆序释放数组的每个元素。
- delete必须指向的是动态分配内存创建的数组的首元素