动态内存分配,引用和类型转换

本文围绕C++展开,介绍了函数相关知识,包括重载、缺省参数、哑元参数和内联函数;阐述了动态内存分配,对比了C语言和C++的方式;讲解了引用的定义、常引用、引用型参数和返回值等;还介绍了隐式和显式类型转换。最后给出了C++编程的一些建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,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;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒听雪落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值