一,C++的函数
1. 函数重载
draw(int x,int y,int r){...}
draw(int x,int y,int w,int h){...}
1)函数重载的定义
在相同作用域中,定义同名的函数,但是它们的参数表必须有所区分,这样的函数构成重载关系。重载与函数的返回类型无关,与参数名也无关,而只与参数的个数,参数类型和顺序有关。
2)调用重载函数时,编译器根据实参与形参的匹配程序,自动选择最优的重载版本。
g++编译器的一般规则:
完全匹配>常量转换>升级转换>降级转换>省略号匹配
3)函数重载原理
C++编译器通过对函数进行换名,将参数表的信息整合到函数名中,实现解决函数重载与名字冲突的矛盾。
函数声明中加入extern "C”要求C++编译器不对函数做换名,便于C程序调用该函数。
extern "C" void func(..){...}
extern "C"
{
void func1(...);
void func2(...);
}
2. 函数的缺省参数
1)可以为函数的部分或全部参数指定缺省值,调用该函数时,如果不给实参,就取缺省值作为相应的形参值。
void func(int a,int b,int flag=缺省值){}
void func(int a,int b,int flag=0){...}
int main()
{
func(10,20);
}
2)缺省参数必须靠右,如果一个参数有缺省值,那么这个参数右侧的所有参数都必须带有缺省值。
void func(int a = 10,int b){}//error
void func(int a = 10,int b = 100 ,int flag = 0){...}
3)如果函数的定义和声明分开,缺省参数应该写在函数的声明部分,而定义部分不写。
void func(){} -->函数的定义
void func(); -->函数的声明
#include <iostream>
using namespace std;
/*char->int,升级转换*/
void bar(int i)
{
cout << "bar(1)" << endl;
}
/*char->const char,常量转换*/
void bar(const char c)
{
cout << "bar(2)" << endl;
}
/*short->char 降级转换*/
void func(char c)
{
cout << "func(1)" << endl;
}
/*short->int 升级转换*/
void func(int n)
{
cout << "func(2)" << endl;
}
/*short->long long 过分的升级转换*/
void func(long long ll)
{
cout << "func(3)" << endl;
}
/*省略号匹配,优先级最低*/
void hum(int i,...)
{
cout << "hum(1)" << endl;
}
/*double -> int ,降级转换*/
void hum(int i,int j)
{
cout << "hum(2)" << endl;
}
int main(void)
{
char c = 'A';
bar(c);
short s = 100;
func(s);
hum(200,1.234);
return 0;
}
3.函数的哑元参数
1)只有类型没有变量名的形参称为哑元
void func(int){...}
2)为了兼容旧代码
算法库:void math_func(int a,int b){...}
使用者:
int main()
{
math_func(num1,num2);
...
math_func(num1,num2);
}
升级算法库:
void math_func(int a,int/*哑元*/){...}
int main()
{
math_func(num1,num2);
...
math_func(num1,num2);
}
3)运算符函数重载:区分前后++、--
#include <iostream>
using namespace std;
void foo(int a,int b=200,int c=300);
/*注意调用时歧义错误*/
//void foo(int a){}
int main(void)
{
foo(10,20,30);
foo(10,20);
foo(10);
}
void foo(int a,int b/*=200*/,int c/*=300*/)
{
cout << "a=" << a << endl;
cout << "b=" << b << endl;
cout << "c=" << c << endl;
}
4. 内联函数
使用inline关键字修饰的函数,表示这个函数是内联函数,编译器会尝试做内联优化,避免函数调用的开销。
1)多次调用的小而简单函数适合内联
2)调用次数极少或者大而复杂的函数不适合内联
3)递归函数不适合内联
4)内联只是一种建议不是要求,能否内联取决于编译器,有些函数不加inline关键字也会被编译器默认处理为内联
for(int i=0;i<10000;i++)
for(int j=0;j<100;j++){...}
for(int i=0;i<100;i++)
for(int j=0;j<10000;j++){...}
二、C++动态内存分配
C语言:malloc()/free()
C++中:new/delete 运算符
new运算符用于动态内存的分配,delete运算符用于动态内存的释放。
C语言:
int *p = (int*)malloc(sizeof(int));
*p = 100;
free(p);
C++:
int *p = new int(200);
//*p = 200;
delete p;
int* pa = new int[10];
pa[0] = 10;
pa[1] = 20;
....
delete[] pa;
#include <iostream>
using namespace std;
int main(void)
{
int* p1 = new int;
cout << *p1 << endl;//0
*p1 = 100;
cout << *p1 << endl;//100
delete p1;
p1 = NULL;
p1 = new int(200);
cout << *p1 << endl;//200
(*p1)++;
cout << *p1 << endl;//201
delete p1;
p1 = NULL;
int* pa = new int[10];
/*C++2011支持new数组初始化
int* pa = new int[10]{1,2,3,4,5,6,7,8};*/
for(int i = 0;i < 10;i++)
{
pa[i] = i+1;
cout << pa[i] << ' ';
}
cout << endl;
delete[] pa;
pa = NULL;
return 0;
}
#include <iostream>
using namespace std;
int main(void)
{
int* p1 = new int(10);
delete p1;
//p1 = NULL;
//cout << "*p1=" << *p1 << endl;
int* p2 = new int(10);
*p1 = 20;//使用野指针,意外修改了p2数据
cout << "p1=" << p1 << endl;
cout << "p2=" << p2 << endl;
cout << "*p2=" << *p2 << endl;
return 0;
}
三,C++的引用(Reference)
1 定义
引用就是某个变量的别名,对引用的操作与对该变量操作完全相同。
int a = 10;
int& b = a;//b就是a的别名
b++;//等价于a++
cout << a << endl;//11
2 常引用
1)定义引用时加const修饰,即为常引用,不能通过常引用修改引用的目标。
int a = 100;
const int& b = a;//b就是常引用
a = 200;//ok
b = 200; //error
2)普通的引用只能引用左值,常引用既可以引用左值也可以引用右值。
int& b = a;//ok,a是左值
int& b = 10;//error,字面值常量10是右值
const int& r = a;//ok
const int& r = 10;//ok
3)关于左值和右值
左值:可以放在赋值运算符的左边,可以修改,可以被取地址。
int i;
i = 10;
++i;
int* p = &i;
右值:只能放在赋值运算符右边的、不可以被修改,不可以被取地址。
字面值都是右值:
10 = i; --10;int* p = &10;//error
表达式值多为右值:
a + b = c;//error
(a + b)++;//error
int* p = &(a+b);//error
函数的返回值:
int func(void)
{
int a=100;
return a;
}
int main()
{
int res = func();
func() = 10;//error
++func();//error
int* p = &func(); //error
}
3 引用型函数参数
1)引用用于函数的参数,可以修改实参的值,可以减小函数调用的开销,避免虚实结合过程中对实参的复制。
2)引用参数可能意外修改实参的值,如果不希望修改实参本身,可以将形参定义为常引用,提高传参的效率同时还可以接收常量型的实参。
4 引用型函数返回值
1)可以将函数返回类型声明为引用,避免函数返回值带来的内存开销,如果一个函数返回类型被声明为引用,那么该函数的返回值就是一个左值。
2)为了避免在函数外部修改 引用的目标变量,可以为引用附加常属性。
3)不要从函数返回局部变量的引用,因为所引用的内存会在函数返回以后被释放。
4)可以在函数中返回成员变量,静态变量,全局变量。
5.引用和指针
1)引用的本质就是指针
double d = 1.23;
double& rd = d;--》double* const pd = &d;
*pd <==> rd
2)指针可以不做初始化,其目标在初始化以后可以随意修改,而引用必须初始化,且一旦初始化所引用的目标不能再改变。
int a,b;
int* p;
p = &a;//p指向a
p = &b;//p指向b
int& r;//error
int& r = a;//r引用a
r = b;//将b的值赋值给a
3)可以定义指针的指针(二级指针),但是不能定义引用的引用。
int a;
int* p = &a;
int** pp = &p;//ok
int& r = a;
int&& rr = r;//error,C++2011中叫右值引用
4)可以定义指针的引用,但是不能定义引用的指针
int a;
int* p = &a;
int*& rp = p;//ok
int& r = a;
int&* pr = &r;//error
5)可以定义指针数组,但不能定义引用数组
int a,b,c;
int* parr[3]={&a,&b,&c};//ok
int& rarr[3]={a,b,c};//error
6)可以定义数组的引用
int arr[3] = {1,2,3};
int (&rarr)[3] = arr;//ok
//int (*parr)[3] = &arr;//ok
7)和函数指针一样,可以定义函数的引用,其语法特征与指针完全相同。
void func(int a,int b){}
int main(void)
{
void (&rfunc)(int,int) = func;//ok
rfunc(100,200);//ok
}
*8)指针和引用符号停靠
int *p;
int * p;
int* p;//good
int *p,*q;//good
int* p,*q;
---------------
int a = 10;
int& b = a;//good
int & b = a;
int &b = a;
int c = 20;
int &b = a,&d = c;//good
int& b = a,&d = c;
四, 类型转换
1 隐式类型转换
char <--> int
int <--> double
int* --> void*
int a=100;
void *pv = &a;//ok
int *pi = pv;//error
2 显示类型转换
2.1 强制类型转换
int -> char
char c = 'A';
int i = (int)c;//C中形式
int i = int(c);//C++风格形式
2.2 C++增加了四种操作符的形式
1)静态类型转换
目标类型变量=static_cast<目标类型>(源类型变量)
主要用于将void*转换为其它类型的指针。
int a;
void* pv = &a;//隐式
int* pi = static_cast<int*>(pv);//静态转换
double* <--> int*//不能隐式,不能静态
#include <iostream>
using namespace std;
int main(void)
{
int* pi = NULL;
//char c = (int)pi;//C风格
char c = int(pi);//C++风格
void* pv = pi;//隐士转换
pi = static_cast<int*>(pv);//静态转换
//c = static_cast<int>(pi);//无法静态
return 0;
}
2)动态类型转换(后面讲)
目标类型变量=dynamic_cast<目标类型>(源类型变量)
#include <iostream>
using namespace std;
int main(void)
{
/*volatile修饰的变量表示是易变,这样要求编译器每次使用该变量时都要从内存
中读取,而不是取寄存器中的备份,防止编译器优化*/
const volatile int ci = 100;
int* pci = const_cast<int*>(&ci);
*pci = 200;
cout << "ci=" << ci << endl;
cout << "&ci=" << (void*)&ci << endl;
cout << "*pci=" << *pci << endl;
cout << "pci=" << pci << endl;
return 0;
}
3)常类型转换
目标类型变量=const_cast<目标类型>(源类型变量)
主要用于去除一个指针或引用的常属性。
const int a = 10;
const int* p = &a;
*p = 20;//error
int* p2 = const_cast<int*>(p)
*p2 = 20;//ok
const int& r = a;
r = 30;//error
int& r2 = const_cast<int&>(r);
r2 = 30;//ok
using namespace std;
int main(void)
{
int addr = 0x12345678;
void* ps = reinterpret_cast<void*>(addr);
cout << ps << endl;
char Text[]="0001\00012345678\000000000";
struct T
{
char type[5];
char acc[9];
char passwd[7];
};
T* pt = reinterpret_cast<T*>(Text);
cout << pt->type << endl;
cout << pt->acc << endl;
cout << pt->passwd << endl;
return 0;
}
4)重解释类型转换
目标类型变量 =
reinterpret_cast<目标类型>(源类型变量)
a.任意类型的指针或引用之间进行转换。
b.在指针和整型之间的转换。
小结:
1.慎用宏,代之const\enum\inline
#define PAI 3.14
-->const double PAI = 3.14;
#define STATE_SLEEP 0
#define STATE_RUN 1
#define STATE_STOP 2
-->enum STATE{SLEEP,RUN,STOP};
#define max(a,b) ((a)>(b)?(a):(b))
-->inline int max(int a,int b)
{
return a>b?a:b;
}
2 变量随用随声明同时初始化
3 尽量用new/delete取代malloc/free
4 少用void*、指针计算、联合体和强制转换
5 尽量用string表示字符串,少用C风格的char*
四,C++薪资程序
#include <iostream>
using namespace std;
class Employee
{
public:
Employee(const string& name,int id,int grade):m_name(name),m_id(id),m_grade(grade){}
void printInfo(void)
{//打印员工信息
printBasic();//公有信息
printExtra();//特有信息
}
void paySalary(void)
{//计算工资
cout << "工资:" << calBasic()+calMerit() << endl;
}
protected:
double m_attend;//出勤率
private:
double calBasic(void)
{
cout << "出勤天数:" ;
int attend;
cin >> attend;
m_attend = attend/23.0;
m_basic = s_grade[m_grade-1];
return m_basic;
}
virtual double calMerit(void)
{
return m_basic/2;
}
void printBasic(void)
{
cout << "姓名:" << m_name << endl;
cout << "工号:" << m_id << endl;
cout << "职级:" << m_grade << endl;
}
virtual void printExtra(void)
{
cout << "职位:普通员工" << endl;
}
string m_name; int m_id;int m_grade;double m_basic;static double s_grade[8];
};
double Employee::s_grade[]={1000,2000,3000,4000,5000,6000,7000,8000};
//经理
class Manager:virtual public Employee
{
public:
Manager(const string& name,int id,int grade,double bonus):Employee(name,id,grade),m_bonus(bonus){}
protected:
void printExtra(void)
{
cout << "职位:经理" << endl;
cout << "绩效奖金:" <<m_bonus << endl;
}
double calMerit(void)
{
cout << "绩效因数:";
double factor;
cin >> factor;
//绩效奖金*绩效因数*出勤率
return m_bonus*factor*m_attend;
}
private:
double m_bonus;//绩效奖金
};
//技术员
class Technician:virtual public Employee
{
public:
Technician(const string& name,int id,int grade,double allow):Employee(name,id,grade),m_allow(allow){}
protected:
void printExtra(void)
{
cout << "职位:技术员" << endl;
cout << "研发津贴:"<<m_allow << endl;;
}
double calMerit(void)
{
cout << "进度因数:";
double factor;
cin >> factor;
//小时数*研发津贴*进度因数*出勤率
return 8*23*m_allow*factor*m_attend;
}
private:
double m_allow;//研发津贴
};
//技术主管
class TechMngr:public Technician,public Manager
{
public:
TechMngr(const string& name,int id,int grade,double allow,double bonus):
Technician(name,id,grade,allow),Manager(name,id,grade,bonus),Employee(name,id,grade){}
private:
void printExtra(void)
{
Technician::printExtra();
Manager::printExtra();
}
double calMerit(void)
{
return (Technician::calMerit()+ Manager::calMerit()) / 2;
}
};
int main(void)
{
Employee emp("张飞",10010,3);
emp.printInfo();
emp.paySalary();
cout << endl;
Manager mngr("刘备",10000,6,6000);
mngr.printInfo();
mngr.paySalary();
cout << endl;
Technician tech("孔明",10086,7,25);
tech.printInfo();
tech.paySalary();
cout << endl;
TechMngr techmngr("曹操",10088,8,20,8000);
techmngr.printInfo();
techmngr.paySalary();
cout << endl;
return 0;
}