指针 和 函数 (By Jabin)
- C++ Primer Plus 中文 第六版
- Essential C++ 中文版
- 深度探索 C++ 对象模型
- C++ 程序设计语言(1-3)(4) 第四版 英文版
- A Tour of C++ (Second Edition)
- Professional C++ (4th Edition)
-
指针
int* p; int* 是一个指针类型 定义了一个int* 类型的指针
int *p; *p是一个int类型的值
int n = 4; int *p = &n
声明语句中初始化指针,被初始化的是指针,而不是指向它的值。
c++中创建指针,计算机将分配用来存储地址的内存,不会分配用来存储指针所指向的数据的内存***
-
* 与 ++
前缀递增(++)、递减(--)与 * 优先级相同,以从右到左的方式进行结合
后缀递增、递减优先级相同,但比前缀递增、递减的优先级高,以从左往右的方式结合double arr[5] = {1.1, 2.2, 3.3, 4.4, 5.5}; double *ptr = arr; ++ptr; //point to arr[1] *++ptr; //先++后*取值 即指针指向arr[1],再取arr[1]的值,最后ptr指向不变 结果为2.2 ++*ptr; //先取arr[0]的值,再对值进行++操作,最后ptr指向不变,结果为1.1 + 1 = 2.1 (*ptr)++; //带括号优先级高,先取arr[1]的值,再进行++操作,++后置所以结果为 1.1,指针*ptr指向2.1 *ptr++; //先ptr++,再取值,同样因为++后置所以结果为1.1,指针*ptr位置+1,指向2.2
-
函数转换
class Page{ public: Page(); Page(int n); //前面加explicit 禁止隐式转换 operator int() const; } //usage: Page page(10); //int to Page or Page page; page = 10; int n = page; // n = 10;
-
返回对象
Test GetOne(const Test & t1, const Test t2) { if(true) return t1; return t2; } const Test & GetOne(const Test & t1, const Test & t2) { if(true) return t1; return t2; } //Note: //1. 返回对象会调用复制构造函数,返回引用不会,第二个效率高 //2. 引用指向的对象应该在调用函数执行时存在 //3. t1 和 t2 都声明为const引用,所以返回类型是const才匹配
如果返回的对象是被调用函数中的局部变量,则应返回对象,而不是引用,因为函数执行完毕时,局部引用对象将被析构回收
Point Point::Operator+(const Point & p1, const Point & p2) { return Point(p1.x + p2.x, p1.y + p2.y); }
-
模板函数
模板函数(function template)参数列表通常由两种类型构成:
明确的类型
暂缓决定的类型
template <typename T> void Display(string & msg, const vector<T> * vec)
template <typename T> T CalScore(T t1, T t2); template<typename T1, typename T2> auto Sum(T1 t11, T2 t22) -> decltype(t11 + t22); //Usage: 用以上两个函数进行求和运算 int a = 5; double b = 6.0; ::CalScore(a, b); //两个类型不一样,将报错;更改如下 ::CalScore<double>(a, b); //Right //decltype (t1 + t2) t; //make t type the same with (t1 + t2) ::Sum(a, b); //因为t11 和 t22 类型不明 所以使用decltype关键字声明返回类型。因为auto可以自动推断t11 + t22 类型,所以也可以去掉 decltype 声明
-
inline函数
内联函数的编译代码与其他程序代码 “内联” 起来了。 也就是说,编译器将使用相应的函数代码替换函数调用。程序无需跳到另一个位置处执行代码,再跳回。因此,运行速度稍快,但是占用更多内存。
声明一个 inline 函数,只需在函数前加关键字 inline 即可,如:
inline int Sum(int a, int b);
将函数声明为 inline 函数,只是对编译器提出要求,编译器是否执行我们的要求,还需视编译器而定。
那些体积小,常被调用,从事计算量不复杂的函数常被声明为 inline 函数
-
虚函数
在函数前关键字 virtual 定义函数为虚函数。
如果方法是通过引用或者指针调用的,没有关键字 virtual 时,程序将根据引用的类型或者指针类型选择方法;如果有关键字virtual,程序将根据引用或指针指向的对象类型来选择方法。
通常编译器处理虚函数会给每个对象添加一个隐藏成员。隐藏成员中保存了一个指向函数地址数组的指针。这种数组称为虚函数表(virtual functions table, vtbl)。虚函数表中存储了为类对象进行声明的虚函数的地址。调用虚函数时,程序将查看存储在对象中的vtbl地址,然后转向相应的函数地址表。
使用虚函数在内存和执行速度方面有一定成本:
- 每个对象将增大,增大量为存储空间
- 对于每个类,编译器都会创建一个地址表
- 对于每个函数调用,都需需要到 vtbl 中查找地址
通常给基类提供一个虚析构函数,即使并不需要
Animal.h class Animal { private: std::string name; int legs; public: Animal(const std::string& name, const int& legs); std::string GetName() const; int GetLegs() const; virtual void ShowInfo(); };
Horse.h class Horse : public Animal { private: int height; public: Horse(const std::string& name, const int& legs, const int& height); virtual void ShowInfo(); //void ShowInfo() override };
Animal.cpp void Animal::ShowInfo() { std::cout << "Animal's name: " << name << ", it has " << legs << " legs" << std::endl; }
Horse.cpp Horse::Horse(const std::string& name,const int& legs,const int& height) : Animal(name, legs) { this->height = height; } void Horse::ShowInfo() { std::cout << "Animal's name: "<<GetName() << ", it has " << GetLegs() << " legs, it's height: "<< this->height << std::endl; }
- Usage
Animal animal("sheep", 4); Horse horse("horse", 4, 1); animal.ShowInfo(); horse.ShowInfo(); Animal& an = animal; Animal& ho = horse; an.ShowInfo(); ho.ShowInfo(); Animal* ani = &animal; Animal* hor = &horse; ani->ShowInfo(); hor->ShowInfo();
- virtual void ShowInfo()
Animal's name: sheep, it has 4 legs Animal's name: horse, it has 4 legs, it's height: 1 Animal's name: sheep, it has 4 legs Animal's name: horse, it has 4 legs, it's height: 1 Animal's name: sheep, it has 4 legs Animal's name: horse, it has 4 legs, it's height: 1
- void ShowInfo() 去掉 virtual 时
Animal's name: sheep, it has 4 legs Animal's name: horse, it has 4 legs, it's height: 1 Animal's name: sheep, it has 4 legs Animal's name: horse, it has 4 legs Animal's name: sheep, it has 4 legs Animal's name: horse, it has 4 legs
如果要重新定义继承方法,要确保与原型完全相同。像 ShowInfo(Animal &animal) 将会覆盖 ShowInfo(). 如果返回类型是基类引用或指针,则可修改为指向派生类的引用或指针(新出现的特例)。这种特性称为 返回型协变(covariance of return type), 因为允许返回类型随类型变化而变化。如果不与原型相同,则原有函数将被隐藏,派生类对象无法访问。
-
虚析构函数
virtual ~Animal() = default
- 使用虚析构函数可以确保正确的析构函数被调用
-
静态/动态联编
将源代码中函数调用解释为执行特定的函数代码块称为函数名联编(binding)。