@
目录因为本人的C++基础不是很扎实,很多面向对象的基本语法掌握的不是很熟练,导致写程序出错时,经常会犯错,于是就去网上找到了侯捷老师的C++课进行观摩学习。
本文是在观看完侯捷老师的《面向对象高级开发》课程后一些笔记和心得。文中老师一直强调要养成写C++大气、正规、高效的编程方法,实际就是要培养良好的编程习惯。
例如在定义头文件时,都使用
#ifndef #define //... #endif
为的就是防止头文件被重复包含。
inline:在类内定义的函数,都默认为内联函数,类外的函数,则需要在返回类型前添加inline。
inline只是建议编译构建函数时,构建为内联的,具体是不是则由编译决定。
inline作用:空间换时间,加快程序的运行速度。
一般使用初始化列表(initialization list)来初始化参数。
没有在初始化列表中的成员参数会被隐式初始化
构造函数一般是声明为public,供他人创建,也有声明为private的,例如单例模式。
class A{ public: A(int _data):m_data(_data){} private: int m_data; };
单例模式:
class{ public: static A& getInstance(){//调用函数时,才创建对象 static A a; return a; } private: A(){} };
#include <iostream> int g_x = 6; class A{ public: // A(){//错误构造函数 // m_data = 5; // m_data2 = g_x; // } A(int x):m_data(x),m_data2(g_x){//正确的构造函数 } private: const int m_data; int& m_data2; }; int main(){ return 0; }
#include <iostream> class A{ public: A(int _data):m_data1(data2),m_data2(_data){} private: int m_data1, m_data2; }; int main(){ return 0; }
不带任何参数的构造函数。
下面的代码会引起歧义:
因为默认构造函数和带默认实参的构造函数等价,编译器调用时,不知道调用哪一个
#include <iostream> class A{ public: A(int _data = 0):m_data(_data){}//默认实参 default argument A(){}//默认构造 private: int m_data; }; int main(){ A a;//编译不通过 return 0; }
使用默认实参的注意事项:
int f(int a = 1, int b, int c = 3);//错误 int f(int a, int b = 2, int c );//错误
f(,2 ,3);//错误 f(1,,3);//错误
类中数据一般都使用private,来封装数据,防止他人随意更改。
如果需要提供读取功能,使用存取器函数,例如下面的getData函数。
class A{ public: int getData() const{return m_data;} private: int m_data; };
格式(下面的const):
return-type functionName(param ) const{}
作用:表示该函数不会改变成员变量的值。
例如下面的例子,常量对象a只能调用常量函数,如果调用非常量函数,就意味着可能会改变对象中成员的值,因为会报错。
#include <iostream> class A{ public: A(int _data = 0):m_data(_data){} int print(){return m_data;} int print2() const { return m_data;} private: int m_data; }; int main(){ const A a(6); // a.print(); //错误的用法 a.print2(); return 0; }
类型 | const object | non-const object |
---|---|---|
const成员函数 | true | false |
no-const成员函数 | false | true |
函数属性加了const后,就是两个两个不同函数(函数签名不同,不考虑返回类型)。
例如:
char operator[](size_type pos) const{} reference operator[](size_type pos){}
当成员函数的const和non-const版本同时存在时,const object只会调用const版本,non-const object只会调用non-const版本
两种形式:pass by value(值传递)/pass by reference(引用传递)
#include <iostream> class A{ public: A(int _data = 0):m_data(_data){}//默认实参 default argument A& operator++(){//前++,可以作为左值 this->m_data++; return *this; } A operator++(int){//后++,因为为临时对象,无法作为左值 A tempA(m_data); m_data++; return tempA; } void getData()const { std::cout<<m_data<<std::endl;} private: int m_data; }; int main(){ A a; a++; a.getData(); (a++).getData();//返回的还是1 //测试临时对象 int n = 0; //(n++) = 3;//报错,因为返回的是一个临时对象 ++n = 4; return 0; }
A& operator++()
函数。inline A& (){//返回类型为接受者 //... return *this;//传递者 }
赋予其他函数或者类访问类内部protected
或者private
成员的访问权限,打破了类的封装。
相同类class的各object互为友元。
分类:成员函数/非成员函数。
区别:只有非静态成员函数中有this指针。
使用非成员函数的情况:
#include <iostream> #include <ostream> class A{ public: A(int _data = 0):m_data(_data){}//默认实参 default argument std::ostream&operator<<(std::ostream& os){ os<<this->m_data; return os; } void printData()const { std::cout<<m_data<<std::endl;} int getData()const { return m_data;} private: int m_data; }; std::ostream& operator<<(std::ostream& os, A& a){//为了可以使使用多个<<符号,因此返回类型为ostream os<<a.getData(); return os; } int main(){ A a(2); //使用成员函数 a.operator<<(std::cout)<<std::endl;//与下面等同 a<<std::cout<<std::endl; //使用非成员函数 std::cout<<a<<std::endl; return 0; }
创建的临时对象,到下一行时,就会自动销毁,不能返回引用。
“三位一体原则”:如果类需要一个析构函数,那它同时可能也需要一个拷贝构造函数和一个赋值运算符成员函数。
测试例子:
#include <iostream> #include <cstring> class A{ private: char* m_data; public: A ():m_data(nullptr){}//默认构造 A (const char* _data){//实参构造 //(不使用初始化列表,是是因为初始化列表不能给m_data分配内存,只会让两个指针指向同一块地址) this->m_data = new char[sizeof(_data)]; std::strcpy(this->m_data, _data); } //实参构造,当_data是常量时,不会分配内存,会导致delete析构函数报错 //只会拷贝指针,使得两个指针指向同一对象,推荐使用在函数体内初始化 // A (char* _data):m_data(_data){ }//带初始化列表的构造函数 //推荐,来代替上面的方法 A(char* _data){//实参构造 this->m_data = new char[sizeof(_data)]; std::strcpy(this->m_data, _data); } A (const A& a){//拷贝构造 this->m_data = new char[sizeof(a.m_data)]; std::strcpy(this->m_data, a.getData()); } A&operator=(const A& a){//赋值运算符函数 if(&a != this){ delete[] this->m_data; this->m_data = new char[sizeof(a.getData())]; std::strcpy(this->m_data, a.getData()); } return *this; } ~A(){//析构函数 std::cout<<"调用析构函数"<<std::endl; delete[] this->m_data; } char* getData() const { return m_data;} void getDataAddress() const {std::cout<<&(m_data)<<std::endl;}//打印地址 void print() {std::cout<<this->m_data<<std::endl;} }; int main(){ //不推荐使用此定义,下面初始化后,指针可以指向不同字符常量,但是指针不能对所指对象进行修改 //char* data = "123456";// warning: ISO C++11 does not allow conversion from string literal to 'char *' //推荐使用 //const char* data = "123456"; char* data = new char[10];//分配内存 for(int i = 0; i < 9;i++){//初始化 *(data + i) = i + '0'; } A a(data);//实参构造 a.print(); A b(a);//拷贝构造 b.print(); A c;//默认构造 c = a;//赋值运算符函数 c.print(); //用于测试带初始化列表的构造函数 //检验指针是否指向同一对象(不要被指针地址的值不同的表面现象所迷惑): //法一: if((char*)a.getData() == (char*)(data)){ std::cout<<"两指针指向同一对象"<<std::endl; }else std::cout<<"两指针指向不同对象"<<std::endl; //法二: for(int i = 0; i < 9;i++){//初始化 *(data + i) = i + 1 + '0'; } a.print(); //打印地址值 std::cout<<&data<<std::endl; a.getDataAddress(); delete[] data; return 0; }
在写管理资源(如内存资源)的类时,如果没有定义析构函数,默认析构函数将会被调用。这个默认析构函数会仅删除指向对象的指针,而删除一个指针不会释放指针指向对象占用的内存,最终会导致内存泄露。
如果只提供一个析构函数,而不显示写出复制构造函数与赋值运算符函数,那情况可能更糟糕。如果调用默认的构造函数,进行的是指针传递而不是值传递,导致两个对象共享一个内存空间,当其中一个对象被删除后,析构函数将释放那片共享的内存空间,接下来对这片已经释放了内存的任何引用都将会导致不可遇见的后果。
进行赋值操作符函数编写时,要进行自我赋值判断,如果不是自我赋值,才删除原对象并释放内存,然后复制新对象。
如果去掉这个判断,会造成将左操作数对象的元素删除并释放其占用的内存的同时,由于左右操作数指向同一对象,导致右操作数同时被删除。但还要将右操作对象复制,这将会带来灾难。
例如:
template<class T> Vec<T>& Vec<T>::operator=(const Vec& rhs){ if(&rhs != this){//进行字符赋值判断 uncreate();//删除运算符左测的数组 create(rhs.begin(), rhs.end());//从右侧元素复制到左侧 } return *this; }
在栈中:
全局数据段:
堆:
例子:
#include <iostream> #include <cstdlib> class A{ public: A(){std::cout<<"constructor:"<<static_cast<void*>(this)<<std::endl;} ~A(){std::cout<<"destory:"<<static_cast<void*>(this)<<std::endl;} void* operator new(size_t size){ std::cout<<"new"<<std::endl; return malloc(size); } void operator delete(void* ptr){ std::cout<<"delete"<<std::endl; free(ptr); } void* operator new[](size_t size){ std::cout<<"new[]"<<std::endl; return malloc(size); } void operator delete[](void* ptr){ std::cout<<"delete[]"<<std::endl; free(ptr); } }; int main(){ A* a = new A(); delete(a); A* b = new A[3]; delete[] b; return 0; }
程序输出:
new constructor:0x7fe643c05960 destory:0x7fe643c05960 delete new[] constructor:0x7fe643c05978 constructor:0x7fe643c0597c constructor:0x7fe643c05980 destory:0x7fe643c05980 destory:0x7fe643c0597c destory:0x7fe643c05978 delete[]
如上所示,在调用A* a = new A;
时,编译器实际执行的操作为:
void* tempP = operator new(sizeof(A));
a = static_cast(A*)(tempP);
a->A::A();
在调用delete a
时,编译器实际进行的操作为:
a->A::~A();
当使用new[]和delete[]时,构造和析构的顺序相反。
静态的作用:
#include <iostream> #include <cstdlib> class A{ public: A(){} static void func(){ m_data = 2; //m_data2 = 3;//error: invalid use of member 'a' in static member function } void print(){ std::cout<<m_data<<std::endl; } private: static int m_data; int m_data2; }; int A::m_data = 1;//通过类名调用 int main(){ A a; a.print(); ((A*)0)->func();//静态函数 a.print(); a.func();//通过对象名调用 a.print(); return 0; }
test.h
#ifndef ALGORITHM_TEST_H #define ALGORITHM_TEST_H static double g_grade;//不初始化时,默认为0 extern double g_grade2; //double g_grade3;//错误:ld: 1 duplicate symbol for architecture x86_64 void func2(); #endif //ALGORITHM_TEST_H
test.cpp
#include "test.h" #include <iostream> double g_grade2 = 100; void func2(){ //全局静态变量 //打印默认值 std::cout<<"test.cpp--->g_grade全局静态变量赋值前:"<<g_grade<<std::endl;//g_grade:0 //打印复印后的值 g_grade = 92; std::cout<<"test.cpp--->g_grade全局静态变量赋值后:"<<g_grade<<std::endl;//g_grade:92 std::cout<<"test.cpp--->g_grade2全局变量:"<<g_grade2<<std::endl;//g_grade2:96.8 // std::cout<<"g_grade3:"<<g_grade3<<std::endl; }
main.cpp
#include <iostream> #include <cstdlib> #include "test.h" int func1(){//静态局部变量 static int n = 0; n++; return n; } int main(){ //局部静态变量 std::cout<<"局部静态变量:"; for(int i = 0;i < 5;++i){ std::cout<<func1()<<" "; if(i == 4) std::cout<<std::endl; } //全局变量+全局静态变量 std::cout<<"全局变量:"<<std::endl; func2(); std::cout<<"main.cpp--->g_grade全局静态变量赋值前:"<<g_grade<<std::endl;//g_grade:0 std::cout<<"main.cpp--->g_grade2全局变量赋值前:"<<g_grade2<<std::endl;//g_grade2:100 g_grade = 96.8; g_grade2 = 96.8; // g_grade3 = 98.7; std::cout<<"main.cpp--->g_grade全局静态变量赋值后:"<<g_grade<<std::endl;//g_grade:96.8 std::cout<<"main.cpp--->g_grade2全局变量赋值后:"<<g_grade2<<std::endl;//g_grade2:96.8 //std::cout<<"g_grade3:"<<g_grade3<<std::endl; return 0; }
输出:
局部静态变量:1 2 3 4 5 全局变量: test.cpp--->g_grade全局静态变量赋值前:0 test.cpp--->g_grade全局静态变量赋值后:92 test.cpp--->g_grade2全局变量:100 main.cpp--->g_grade全局静态变量赋值前:0 main.cpp--->g_grade2全局变量赋值前:100 main.cpp--->g_grade全局静态变量赋值后:96.8 main.cpp--->g_grade2全局变量赋值后:96.8
#ifndef ALGORITHM_TEST_H #define ALGORITHM_TEST_H void func2(); static void func3(); #endif //ALGORITHM_TEST_H
test.cpp
#include "test.h" #include <iostream> void func2(){ } void func3() { std::cout<<"test.cpp--->全局静态函数:func3()"; }
main.cpp
#include <iostream> #include <cstdlib> #include "test.h" //void func2(){//错误:ld: 1 duplicate symbol for architecture x86_64 // //} //可以在这里重复定义func3 void func3(){ std::cout<<"main.cpp--->全局静态函数:func3()"; } int main(){ func3();//要调用此函数,main.cpp中必须定义func3,其他源文件中定义无效 return 0; }
为了区分不同库中相同名称的函数、类、变量,使用了命名空间即定义了上下文。
#include <iostream> namespace first_space{ void func() { std::cout << "first_space:func()"<<std::endl; } } namespace second_space{ void func() { std::cout << "second_space:func()"<<std::endl; } } int main(){ first_space::func(); second_space::func(); return 0; }
using namespace std;
using std::cout;
关系:has-a
#include <iostream> class Son{ public: Son(){std::cout<<"Son construction"<<std::endl;} ~Son(){std::cout<<"Son destroy"<<std::endl;} }; class Mother{ public: Mother(){std::cout<<"Mother construction"<<std::endl;} ~Mother(){std::cout<<"Mother destroy"<<std::endl;} private: Son son; }; int main(){ Mother mother; return 0; }
输出:
Son construction Mother construction Mother destroy Son destroy
一种通过引用的特殊的复合
Pimpl(pointer to implementation):母类只是对外的接口,真正的实现都在子类里,当母类需要动作的时候,就去调用子类。
作用:母类有一个指针去指向实现所有功能的子类,这种手法的好处在于,这个指针还可以去指向不同的实现类,去实现不同的功能,子类不管怎么变动都不影响s母类也就不影响客户端,母类也永远不用再编译。
示例:
#include <iostream> class Mother; class Son{//真正的实现 friend class Mother; public: Son(int data = 0):m_data(data){std::cout<<"Son construction"<<std::endl;} ~Son(){std::cout<<"Son destroy"<<std::endl;} private: int m_data; void setData(int data){m_data = data;} void printData()const {std::cout<<"data:"<<m_data<<std::endl;} }; class Mother{//对外接口 public: Mother(){std::cout<<"Mother construction"<<std::endl;} ~Mother(){std::cout<<"Mother destroy"<<std::endl;} void printData(){son->printData();} void setData(int data){son->setData(data);} private: Son* son;//Pimpl(pointer to implementation) }; int main(){ Mother mother; mother.setData(2); mother.printData(); return 0; }
输出:
Mother construction data:2 Mother destroy
表示:is-a
调用函数时,是在运行时决定,而不是在编译期间决定。
公有继承:所有公有成员和保护成员保持原装
私有继承:所有公有成员和保护成员都成为派生类的私有成员
保护继承:所有公有成员和保护成员都成为派生类的保护成员
条件:
纯函数: 函数变量列表后加 = 0
声明了纯虚函数的类,不能实例化为对象。必须要实现后,才能实例化。
构造顺序:基类---->派生类
析构顺序:派生类--->基类
范例:
#include <iostream> class Base{ protected: //纯虚函数:无法实例化 virtual void printData()const = 0; }; class Father:public Base{//对外接口 public: Father(int data = 0):m_data(data){std::cout<<"Father construction"<<std::endl;} virtual ~Father(){std::cout<<"Father destroy"<<std::endl;} virtual void print(){std::cout<<"Father print()"<<std::endl;} protected://仅继承可以访问 virtual void printData()const{std::cout<<"Father:"<<m_data<<std::endl;};//纯虚函数 private: int m_data; }; class Son: public Father{//真正的实现 public: Son(int data = 0):m_data(data){std::cout<<"Son construction"<<std::endl;} ~Son(){std::cout<<"Son destroy"<<std::endl;} void setData(){printData();} void print(){std::cout<<"Son print()"<<std::endl;} private: int m_data; }; int main(){ Father father; Son son; father.print(); son.print(); //动态绑定 std::cout<<"动态绑定------------->"<<std::endl; Father* fatherP = &father; Father& sonP = son; fatherP->print(); sonP.print(); //显示调用 std::cout<<"显示调用------------->"<<std::endl; sonP.Father::print(); return 0; }
输出:
Father construction Father construction Son construction Father print() Son print() 动态绑定-------------> Father print() Son print() 显示调用-------------> Father print() Son destroy Father destroy Father destroy
作用:在父类中定义处理流程的框架,在子类中实现具体处理。
MFC中经常用到此方法。
#include <iostream> using std::cout; class Base{ private: virtual void calculate() = 0; public: void getResult(){ std::cout<<"satrt:"<<std::endl; calculate();//具体实现放在子类 std::cout<<"end"; } }; class Derived: public Base{ public: virtual void calculate(){ std::cout<<"calculate"<<std::endl; } }; int main( ){ Derived derive; derive.getResult(); return 0; }
输出:
satrt: calculate end
构造顺序:A--->C--->B
析构顺序:B--->C--->A
//计算复合、继承的构造函数先后 #include <iostream> using std::cout; using std::endl; class A{ public: A(){cout<<"构造A "<<endl;} ~A(){cout<<"析构A "<<endl;} }; class C{ public: C(){cout<<"构造C "<<endl;} ~C(){cout<<"析构C "<<endl;} }; class B :public A{ public: B(){cout<<"构造B "<<endl;} ~B(){cout<<"析构B "<<endl;} private: C c; }; int main( ){ B b; return 0; }
输出:
构造A 构造C 构造B 析构B 析构C 析构A
解决的问题:一个文件,四个窗口,当文件变化时,窗口也会跟着改变。
设计方法:
#include <iostream> #include <vector> //前置声明 class Observer; //数据 class Subject{ private: int m_value; std::vector<Observer*> m_views; public: void attach(Observer* objs){ m_views.push_back(objs); } void setVal(int value){ m_value = value; notify(); } //直接在类内定义会报错:error: member access into incomplete type void notify(); }; //视图 class Observer{ public: virtual void update(Subject* subject, int value) = 0; }; void Subject::notify() { for(int i = 0;i < m_views.size();++i){ m_views[i]->update(this, m_value); } } int main( ){ return 0; }
解决问题:文件系统,里面有文件夹或文件
案例:
//文件系统
#include <iostream> #include <vector> class Component{ public: Component(int data):m_data(data){} virtual void add(Component*){} private: int m_data; }; //文件 class Primitive:public Component{ public: Primitive(int data):Component(data){} }; //文件夹 class Composite:public Component{ public: Composite(int data):Component(data){} void add(Component* elem){m_p.push_back(elem);} private: std::vector<Component*> m_p; }; int main( ){ return 0; }
作用:用于创建重复的对象,同时又能保证性能。
解决问题:创建未来才会出现的子类,让子类自己创建自己(原型),然后基类可以看见原型并复制它。
案例:
#include <iostream> #include <vector> enum imageType{ LSAT,SPOT }; class Image{ public: //发现和复制 static Image* findAndClone(imageType); virtual void draw() = 0; static void printNextSlot(){std::cout<<"m_nextSlot:"<<m_nextSlot<<std::endl;} virtual imageType returnType() = 0; protected: //用于子类返回类型 // virtual imageType returnType() = 0; //用于子类创建自己的原型 virtual Image* clone() = 0; //添加原型 static void addProtoType(Image* image){ m_protoTypes[m_nextSlot++] = image; } private: static int m_nextSlot; static Image* m_protoTypes[10]; }; Image* Image::m_protoTypes[]; int Image::m_nextSlot; class LandSatImage: public Image{ public: imageType returnType(){ return LSAT; } void draw(){ std::cout<<"LandSatImage::draw:"<<m_id<<std::endl; } //调用带参构造 Image* clone(){ return new LandSatImage(1); } protected: //避免addProtoType重复添加自己 //只用于基类中的clone函数 LandSatImage(int dummy){ m_id = m_cout++; } private: int m_id; static int m_cout; static LandSatImage m_landSatImage; //调用静态的自己 //默认构造 LandSatImage(){ std::cout<<"构造LandSatImage"<<std::endl; addProtoType(this); } }; LandSatImage LandSatImage::m_landSatImage; int LandSatImage::m_cout; class SpotImage: public Image{ public: imageType returnType(){return SPOT;} Image* clone(){ return new SpotImage(1);} void draw(){std::cout<<"SpotImage:draw:"<<m_id<<std::endl;} protected: SpotImage(int dummy){ m_id = m_cout++; } private: int m_id; static int m_cout; static SpotImage m_spotImage; SpotImage(){ std::cout<<"构造SpotImage"<<std::endl; addProtoType(this); } }; SpotImage SpotImage::m_spotImage; int SpotImage::m_cout; Image* Image::findAndClone(imageType type) { for(int i = 0;i < m_nextSlot;++i) if(m_protoTypes[i]->returnType() == type) return m_protoTypes[i]->clone(); } int main( ){ Image* landSatImage = Image::findAndClone(LSAT); Image* spotImage = Image::findAndClone(SPOT); Image::printNextSlot(); std::cout<<"type:"<<spotImage->returnType()<<std::endl; return 0; }
下面的例子是通过类型转换函数,将f转为double类型。
//测试类型转换 #include <iostream> class Franction{ public: explicit Franction(int num, int den = 1):m_numerator(num),m_denominator(den){} //Franction(int num, int den = 1):m_numerator(num),m_denominator(den){} // Franction operator +(const Franction& f){ return Franction(this->m_numerator + f.m_numerator,this->m_denominator + f.m_denominator);} //类型转换函数 operator double(){ return static_cast<double >(m_denominator/m_denominator);} private: int m_numerator; int m_denominator; }; int main(){ Franction f(3,5); //Franction d2 = f + 4; double d2 = 4 + f; return 0; }
下面的例子是将4用构造函数转为Fraction类型,但是如果构造函数使用explicit声明后,将无法隐式转换。
explicit作用:构造函数只是在显示声明时使用,无法进行隐士转换,比如把4自动转为Fraction类型。
//测试类型转换 #include <iostream> class Franction{ public: //使用explicit后,不能进行隐士转换 //explicit Franction(int num, int den = 1):m_numerator(num),m_denominator(den){} Franction(int num, int den = 1):m_numerator(num),m_denominator(den){} //操作符号函数 Franction operator +(const Franction& f){ return Franction(this->m_numerator + f.m_numerator,this->m_denominator + f.m_denominator);} //类型转换函数 // operator double(){ return static_cast<double >(m_denominator/m_denominator);} private: int m_numerator; int m_denominator; }; int main(){ Franction f(3,5); Franction d2 = f + 4;//使用explicit声明构造函数后会报错 //double d2 = 4 + f; return 0; }
转换路线:
//测试类型转换 #include <iostream> class Franction{ public: //使用explicit后,不能进行隐士转换 //explicit Franction(int num, int den = 1):m_numerator(num),m_denominator(den){} Franction(int num, int den = 1):m_numerator(num),m_denominator(den){} //操作符号函数 Franction operator +(const Franction& f){ return Franction(this->m_numerator + f.m_numerator,this->m_denominator + f.m_denominator);} //类型转换函数 operator double(){ return static_cast<double >(m_denominator/m_denominator);} private: int m_numerator; int m_denominator; }; int main(){ Franction f(3,5); Franction d2 = f + 4;//error: use of overloaded operator '+' is ambiguous //double d2 = 4 + f; return 0; }
如果构造函数声明为explicit,那将会报错 error: no viable conversion from 'double' to 'Franction'
,因为对于第一种情况,无法将最终结果转为Franction;对于第二种情况,无法将4转为Franction,因此都将无法完成操作。
#include <iostream> #include <cstdlib> double func(const double& ra) { return ra*ra; } double func2(double& ra){ return ra*ra; } int main(){ //左值 double side = 3.0; double* pd = &side; double& rd = side; //右值 long edge = 5L; double lens[4]={2.3,3.4,4.5,6.7}; //const&类型 //左值参数 double c1 = func(side); // ra 是side double c2 = func(lens[2]); // ra是lens[2] double c3 = func(rd); // ra 是 rd double c4 = func(*pd); // ra 是*pd //右值参数 double c5 = func(edge); // ra 是临时变量(double引用不能指向long) double c6 = func(7.0); // ra 是临时变量 std::cout<<"c5:"<<c5<<std::endl;//c5:25 std::cout<<"c6:"<<c6<<std::endl;//c6:49 //&类型 //右值参数(报错) //double d1 = func2(2.0); // double d2 = func2(edge); return 0; }
重载*、->运算符函数。
#include <iostream> #include <cstdlib> template <typename T> class Share_ptr{ public: Share_ptr(T* p):px(p){} T& operator*() const{ return *px;} T* operator->() const{return px;} private: T* px; }; class A{ }; int main(){ Share_ptr<A> sp(new A); A a(*sp); return 0; }
重载函数调用操作符
#include <iostream> #include <utility> template <typename T> struct identity{ const T&operator()(const T& x) const{ return x; } }; template <typename Pair> struct select1st{ const typename Pair::first_type& operator()(const Pair& x) const{ std::cout<<"operator()"; return x.first; } }; int main(){ std::pair<int, int> aPair; select1st<std::pair<int, int>>()(aPair); return 0; }
继承中的虚函数的继承,就是虚函数的调用权。
下面的表中,例如A中有虚函数,因此类A的内存中,有一个虚指针(vptr),然后虚指针指向虚表(vptr),虚标中存储着函数的地址。