我想总结这些常见的特殊函数类型
函数的重载(founction overloading) 定义:C++允许同一函数名定义多个函数,而这些函数的参数个数和参数类型可以不相同,这就是函数的重载。 即对一个函数名重新赋予它新的含义,使得一个函数名可以多用。所谓重载,其实就是“一物多用”。
不仅函数可以重载,运算符也可以重载。例如>>
和<<
,既可以作为移位运算符也可以作为输入输出流中的插入运算符和数据输入流中的提取运算符。
函数的重载并不要求函数体(即花括号{}里的内容)是相同的。重载函数除了允许参数的类型不同以外,还允许参数的个数不同。
重载函数的参数个数、参数类型或参数顺序三者中必须至少有一种不同,返回值类型可以相同也可以不同。
总结来说函数的重载就是函数名字是相同的。
template <typename T> //模板声明,其中T为类型参数,代表一个虚拟的类型名
T max(T a, T b, T c) //定义一个通用函数,用T作虚拟的类型名
{
if(b>a) a=b;
if(c>a) a=c;
return a;
}
int main()
{
int x=1,y=2,z=3;
max(x,y,z); //调用模板函数,此时T被int取代
}
内存中供用户使用的存储空间的情况:
程序区 |
---|
静态存储区 |
动态存储区 |
变量的存储类别(storage class)共有4种:自动的(auto),静态的(static),寄存器的(register),外部的(extern)。
自动变量(auto)
其实就是函数的形参和在函数中定义的变量(包括在复合语句中如for中定义的变量),都属于自动变量。也就是平常在变量定义时前边不加任何东西的话,系统就默认为是auto自动变量了。
属于一个动态的存储方式,在函数调用或复合语句结束时自动释放空间。其中auto和int的前后顺序随意,auto也可以省略,因此平常使用最多的就是auto了。声明方式:
auto int a,b;
#include
using namespace std;
int f(int a) //定义f函数,a为形参
{
auto int b=0; //定义b为自动变量
static int c=3; //定义c为静态局部变量
b=b+1;
c=c+1;
return a+b+c;
}
int main( )
{
int a=2,i;
for(i=0;i<3;i++)
cout<<f(a)<<″ ″;
cout<<endl;
return 0;
}
运行结果为
7 8 9
其中呢,c是静态局部变量,因此每回调用该函数时只是进行加操作而不赋初值3。
寄存器变量(register)
对于一些使用频繁的变量(比如这个函数中要执行10000次循环,每次循环都要引用某局部变量),则存取变量要花费不少时间,为了提高执行效率,C++允许将局部变量的值放在CPU的寄存器中,需要时直接从寄存器中取出参与运算,不必再到内存中去存取。实际很少用,了解即可。
外部变量(extern)
用extern是为了声明全局变量,以扩展全局变量的作用域。
比如这个已经定义好的全局变量没有在开头定义,在定义之前还想使用该变量就可以用extern来进行提前引用声明,以扩展该变量在程序文件中的作用域。
int main()
{
extern int a,b;//对全局变量a,b作提前引用说明
c=a+b;
return 0;
}
int a=15,b=7;//定义全局变量
static 类型标识符 函数名()
~
符号,在C++中这是位取反运算符,从这点也可想到:析构函数是与构造函数作用相反的函数。在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。
友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。
一个实例:
#include
using namespace std;
class Box
{
public:
// 构造函数定义
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume()
{
return length * breadth * height;
}
int compare(Box box)
{
return this->Volume() > box.Volume();
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main(void)
{
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
if(Box1.compare(Box2))
{
cout << "Box2 is smaller than Box1" <<endl;
}
else
{
cout << "Box2 is equal to or larger than Box1" <<endl;
}
return 0;
}
//Student是基类,Graduate是派生类,他们都有display这个同名的函数
#include
#include
using namespace std;
//------------声明基类Student-------------
class Student
{
public:
Student(int, string, float); //声明构造函数
virtual void display(); //声明虚函数
protected: //受保护成员,派生类可以访问
int num;
string name;
float score;
};
//Student类成员函数的实现
Student::Student(int n,string nam, float s) //定义构造函数
{num=n;name=nam;score=s;}
void Student::display() //定义输出函数
{cout<<"num:"<<num<<"\nname:"<<name<<"\nscore:"<<score<<"\n\n";}
//------------声明公用派生类Graduate---------------
class Graduate:public Student
{
public:
Graduate(int, string, float, float); //声明构造函数
void display(); //定义与基类同名的函数
private:
float wage;
};
//Graduate类成员函数的实现
Graduate::Graduate(int n,string nam, float s,float w):Student(n,nam,s),wage(w){}
void Graduate::display()
{cout<<"num:"<<num<<"\nname:"<<name<<"\nscore:"<<score<<"\nwage:"<<wage<<endl;}
//--------------主函数-------------------
int main()
{
Student stud1(1001,"Wang",35.2);
Graduate grad1(2001,"Hang",74.2, 1500);
Student *pt=&stud1;
pt->display();//输出基类中数据
pt = &grad1;
pt->display();//输出派生类中数据
return 0;
}
#include
。double a=1.23456789;
cout<<setprecision(4)<<a 表示输出数a的4位有效数字
cout<<setiosflags(ios::fixed)<<setprecision(4)<<a 表示输出数a的4位小数
double b=10;
cout<<setfill('*')<<setw(8)<<b ;输出b的时候占8个位,不够的用*填充
在一个类的内部定义另一个类,我们称之为嵌套类(nested class),或者嵌套类型,外边这个类称为外围类。 之所以引入这样一个嵌套类,往往是因为外围类需要使用嵌套类对象作为底层实现,并且该嵌套类只用于外围类的实现,且同时可以对用户隐藏该底层实现。
虽然嵌套类在外围类内部定义,但它是一个独立的类,基本上与外围类不相关。它的成员不属于外围类,同样,外围类的成员也不属于该嵌套类。嵌套类的出现只是告诉外围类有一个这样的类型成员供外围类使用。并且,外围类对嵌套类成员的访问没有任何特权,嵌套类对外围类成员的访问也同样如此,它们都遵循普通类所具有的标号访问控制。
嵌套类可以直接引用外围类的静态成员、类型名和枚举成员(假定这些成员是公有的)。 类型名是一个typedef名字、枚举类型名、或是一个类名。
嵌套类的特点:
重载来自(overload):指的是同一可访问区内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列确定调用哪个函数,重载不关心函数返回类型。
class A{
public:
void test(int i);
void test(double i);
void test(int i, double j);
void test(double i, int j);
int test(int i); //错误,非重载
};
比如这前四个就是重载函数。
隐藏指的是派生类的函数屏蔽了与其同名的基类函数。 注意只要同名函数,不管参数列表是否相同,基类函数都会被隐藏。
比如以下实例:
#include
using namespace std;
class A
{
public:
void fun1(int i, int j)
{
cout<<"A::fun1():"<<i<< " " <<j<<endl;
}
};
class B:public A
{
public:
//隐藏
void fun1(double i)
{
cout<<"B::fun1():"<<i<<endl;
}
};
int main()
{
B b;
b.fun1(5);//调用B类中的函数
b.fun1(1, 2);//出错,因为基类函数被隐藏
system("pause");
return 0;
}
已存在的类称为基类或父类,比如“马”;
从基类派生出来新建立的类称为派生类或子类,比如“公马”;
从已有的类(父类)产生一个新的类,称为类的派生。
定义派生类的一般形式:
class 派生类名:基类列表
{
成员列表
}
public(公有成员)
: 在类的内部和外部都可以访问的成员。private(私有成员)
: 在类的内部可以访问, 在类的外部不可以访问, 只能通过成员函数或友元函数进行访问。protected(保护成员)
: 与私有成员类似, 但在派生类中可以访问。类之间的三种关系:
class B{ private: A a;}
class B{public: void method(A &a);}
class B: public A{}
重写翻译自(override),也翻译成覆盖,**指的是派生类中存在重新定义的函数。**其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。只有函数体不同(也就是花括号{}内的内容)。
派生类调用时会调用派生类的重写函数,不会调用被重写函数。重写的基类中被重写的函数必须有virtual修饰。
比如以下例子:
#include
using namespace std;
class A{
public:
virtual void fun3(int i){
cout << "A::fun3() : " << i << endl;
}
};
class B : public A{
public:
//重写
virtual void fun3(double i){
cout << "B::fun3() : " << i << endl;
}
};
int main(){
A a;
B b;
A * pa = &a;
pa->fun3(3);
pa = &b;
pa->fun3(5);
system("pause");
return 0;
}
&
与指针*
简述*
: 间接运算符。 用来说明该变量是指针变量。&
: 取址运算符。*&
:表示地址值所代表的内存块的内容。int *p;
中,我们称int是指针变量p的基类型。int k=1,*p; p = &k;//将变量k的地址赋给了p,也就是p指向了变量k。int j = *p;
&k
表示取出变量k
的地址赋给p
,*p
表示取出p
也就是取地址&k
中的内容给了变量j
。p=NULL;
等价于p='\0';
或p=0;
*&i
、(*p)++
、++*p
、*p++
:这些都是自右向左计算的。*p +1
表取指针变量p所指存储单元中的内容后加1*p+=1
可写成(*p)++
或++*p
*++p
表示指针地址向后移一位之后返回;++*p
表示指针所指的值+1返回,比如以下例题很经典:int a = 3;
int *p = &a;
cout << *p;
//输出:3
//原类定义
class CMystring
{
public:
CMystring(char* pData = nullptr);
CMystring(const CMystring& str);
~CMystring(void);
private:
char* m_pData;
}
//定义拷贝(复制)构造函数
CMystring& CMystring::operator == (const CMystring &str)
{
if(this != &str)
{
CMyString strTemp(str);
char* pTemp = strTemp.m_pData;
strTemp.m_pData = m_pData;
m_pData = pTemp;
}
return* this;
}