一 内联函数
(1)函数调用的回顾:
函数的调用有时间和空间上的开销,程序在执行一个函数之前需要做一些工作,实参,局部变量,返回值等若干寄存器入栈,然后才能执行函数体,执行完毕之后,还要回收空间(出栈),等,需要时间和空间上的开销
(2)c语言中
<1> 在复制代码的时候,容易出现意想不到的边界效应
可以使用宏函数来解决中国问题,编译器通过直接替换代码的方式,省去了入栈,出栈等开销,缺点就是缺少边界的检查,但是在效率上还是可取的 #define MAX(A,B) (A > B) ? A : B
<2> 使用宏,无法进行调试
<3> 使用宏,无法访问类的私有成员
(3)c++中
推荐使用内联函数代替宏函数,内联函数,类似于宏函数体的替换,这种在函数调用出内嵌入函数体的函数称为内嵌函数,也成为内联函数
inline int myfun(int a,int b) { return (a > b)? a: b; }
<2> 内联函数的特点和缺点
1> 不能存在任何形式的循环语句 2> 不能存在过多的条件判断语句 3> 函数体不能过于庞大(内联函数省去了函数调用时入栈,出栈的等开销) 4> 不能对函数进行取地址操作 5> 函数内联声明必须在调用语句之前 总结:内联函数省去了函数调用时入栈,出栈的等开销,所以当函数体的开销远大于函数的入栈,出栈,跳转等开销时,内联函数就变得没有意义。
#include <iostream> using namespace std; #define MAX(A,B) A > B ? A :B inline int find_max(int a,int b) { return a > b ? a : b; } int main(int argc, const char *argv[]) { int a = 2,b = 2; cout<<find_max(++a,b)<<endl; cout<<a<<" "<<b<<endl; cout<<"***********************"<<endl; int c = 2,d =2; int ret = MAX(++c,d); //operator<<(cout,A > B ? A :B) cout<<"ret = "<<ret<<endl; return 0; }
二 c++中默认参数
(1)概念
c++中,允许定义函数参数的时候给其一个默认值,在使用该函数的时候,如果不传参,可以直接使用该默认值
(2)例
#include <iostream> using namespace std; //默认参数在定义的时候,从左往右,只要有一个参数为默认参数,那么它右边所有参数都必须为默认参数 //int MAX(int a,int b = 1,int c) int MAX(int a,int b,int c = 100) { cout<<a<<" "<<b<<" "<<c<<endl; } int main(int argc, const char *argv[]) { int x = 1,y = 2; MAX(x,y); //如果未传参,直接使用默认参数,如果传参,则使用传的参数 return 0; }
三 占位参数
(1)概念
占位参数只有参数类型的声明,没有参数变量的声明,一般情况下,无法在函数体的内部使用占位参数
(2)例
#include <iostream> using namespace std; struct A { unsigned int a:10; unsigned int :20; unsigned int c:2; }; void add(int a,int b,int = 0) //有时候,占位参数也和默认参数一起使用 { cout<<"a + b ="<<a+b<<endl; } int main(int argc, const char *argv[]) { add(1,2); cout<<sizeof(A)<<endl; return 0; }
四 函数重载(重点)
(1) 概念
在实际开发的时候,有时候需要实现几个功能类似的函数,在c语言中,不得不定义多个函数名不同的函数去实现它 在c++中,可以利用函数重载,实现一类函数功能,使用同一个函数名实现。
(2) 函数重载的条件
1. 函数名相同 2. 函数的参数 类型,个数,顺序不同 3. 和返回值类型无关
(3) 例
#include <iostream> using namespace std; void Swap(int &x,int &y) { cout<<"int int"<<endl; int tmp = x; x = y; y = tmp; } /*int Swap(int &x,int &y) //和返回值类型p关 { cout<<"int int"<<endl; int tmp = x; x = y; y = tmp; }*/ void Swap(int &x,double &y) //参数的类型不同 { cout<<"int double"<<endl; int tmp = x; x = y; y = tmp; } void Swap(int &x,int &y,int &z) //参数的个数不同 { cout<<"int int int"<<endl; int tmp = x; x = y; y = tmp; } void Swap(double &x, int &y) //参数的顺序不同 { cout<<"double int"<<endl; int tmp = x; x = y; y = tmp; } int main(int argc, const char *argv[]) { int a = 100,b = 200,d = 300; double c = 1.2346; cout<<"a = "<<a<<" b = "<<c<<endl; //Swap(a,b); Swap(a,c); cout<<"a = "<<a<<" b = "<<c<<endl; return 0; }
五 函数重载的二义性
#include <iostream> using namespace std; void add(int a,int b) { cout<<a+b<<endl; } void add(int a,int b,int c = 100) { cout<<a+b<<endl; } int main(int argc, const char *argv[]) { add(1,2); return 0; }
六 c++的动态内存分配
(1) new /delete
c语言中:使用malloc、free函数 c++中:使用new/delete关键词,但是malloc和free仍然可以使用
(2) 两者区别(*)
c语言:malloc是一个函数,返回值类型是void*类型。
c++中:new和delete是关键词,返回值类型是申请对象的类型,可以初始化。
(3) 例:
#include <iostream> using namespace std; int main(int argc, const char *argv[]) { int *p = new int; //分配1个int型的空间,返回值是(int *) *p = 100; cout<<"*p = "<<*p<<endl; delete p; //释放p指向的内存空间 p = NULL; int *q = new int(200); //申请1个int,并且对其初始化为200 cout<<"*q = "<<*q<<endl; delete q; q = NULL; int *q2 = new int[5]{1,2,3,4,5}; //c++11新特性,c++11千不可以初始化动态数组 for(int i = 0 ; i < 5;i++) { cout<<q2[i]<<" "; } cout<<endl; delete []q2; q2 = NULL; return 0; }
七 多维数组的创建和释放
例:二维数组的动态创建,例如申请存放二维数组int a[3][4]的空间
#include <iostream> using namespace std; int main(int argc, const char *argv[]) { int **a = new int *[5]; for(int i = 0 ; i < 5;i++) { a[i] = new int[6]; } //使用delete进行内存释放,只要将顺序反过来就行 for(int i = 0 ; i < 5;i++) { delete []a[i]; } delete []a; return 0; }
练习:
定义一个记录学生信息的结构体,包含姓名,年龄,成绩三个成员,键盘输入5个学生的信息,找到成绩低于平均分的学生,并输出其信息。
#include<iostream>
using namespace std;
typedef struct student
{
char name[20];
int age;
int grade;
}student;
void printfmessage(student *stu,int i)
{
cout<<"第"<<i+1<<"个学生"<<endl;
cout<<"姓名:"<<stu->name<<" "<<"年龄:"<<stu->age<<" "<<"成绩:"<<stu->grade<<endl;
}
int main(int argc, char const *argv[])
{
int i;
double a=0;
student *stu=new student[5];//学生数组
for(i=0;i<5;i++)
{
cout<<"请输入第"<<i+1<<"个学生信息"<<endl;
cin>>stu[i].name;
cin>>stu[i].age;
cin>>stu[i].grade;
}
for(i=0;i<5;i++)
{
printfmessage(&stu[i],i);
}
for(i=0;i<5;i++)
{
a=a+stu[i].grade;
}
a=a/5;
for(i=0;i<5;i++)
{
if(stu[i].grade<a)
{
cout<<"姓名"<<stu[i].name<<" "<<"年龄"<<stu[i].age<<" "<<"成绩"<<stu[i].grade<<endl;
}
}
return 0;
}
八 类和对象
8.1 面向对象编程
优点:
1.开发速度快,如果开发某个功能,实现起来很麻烦的话,可以使用现有的类快速实现 2.封装性和抽象性:结构清晰,很标准,规范化,易于理解,可读性强 3.继承:便于扩展,改动小,大框架不需要改动。 4.易维护:维护成本低。 5.质量高:被反复测试,可以快速的满足各项功能开发的需求 6.效率高:依赖于抽象,对于具体的实现统一了接口
缺点:
相比于c语言,运行效率会下降10%左右。
8.2 类和对象的使用
#include <iostream> #include <cstring> using namespace std; struct Student //默认是公有权限 { int age; char *name; }; class Student1 //默认是私有权限,只能在类的内部访问 { public: //声明下面的成员为公有属性 int age; //类的成员变量 属性 char *name; void print() //类的成员函数 方法 { cout<<"age = "<<age<<" name = "<<name<<endl; } }; int main(int argc, char const *argv[]) { /* Student s1; s1.age = 1; */ Student1 s1; s1.age = 1; s1.name = new char[32]; strcpy(s1.name,"zhangsan"); s1.print(); return 0; }
8.3 类的访问权限
访问级别由 public protected private 三个关键词组成,在使用三个关键词前,我们首先要了解类的外部和类的内部的概念。
public:公有属性,凡是在它下面定义的,类的内部和外部都可以访问 protected::保护属性,凡是在它下面定义的,在继承关系中,可以在子类中访问 private:私有属性,凡是在它下面定义的,只能在类的内部访问。
#include <iostream> using namespace std; class Test { private: //私有属性,只能在类的内部访问 int a; void f1(){} protected://保护权限,用于继承,类的外部不能访问 int b; void f2(){} public: //公有权限,类的内部和外部都可以访问 int c; void f3(){} private: //同一个关键词可以出现多次 int e; }; int main(int argc, char const *argv[]) { Test t1; //Test:类 t1:对象 //t1.a = 1; //私有成员不能在类的外部访问 t1.c = 200; return 0; }
8.4 类中的元素说明
#include <iostream> using namespace std; class Circle { private: int m_r; //属性 double m_s; public: void SetR(int r) //成员函数 方法 { m_r = r; } double Gets() { m_s = 3.14*m_r*m_r; return m_s; } }; int main(int argc, char const *argv[]) { Circle c1; //创建对象 c1.SetR(100); cout<<c1.Gets()<<endl; return 0; }
8.5 类的使用案例
#ifndef _STUDENT_H_ #define _STUDENT_H_ #include <iostream> using namespace std; class Student { public: /** * @brief Get the Age object * * @return int */ int GetAge(); int GetID(); void SetAge(int a); void SetID(int i); private: int m_age; int m_id; }; #endif
#include "student.h" int Student::GetAge() { return m_age; } int Student::GetID() { return m_id; } void Student::SetAge(int a) { m_age = a; } void Student::SetID(int i) { m_id = i; }
#include "student.h" int main(int argc, char const *argv[]) { Student s1; s1.SetAge(18); s1.SetID(19); cout<<s1.GetAge()<<endl; return 0; }
练习
设计一个圆形类和一个点类,计算点在圆外,园内,还是圆上(点和圆的关系)
1>点: 属性:横坐标和纵坐标 方法:点和点的距离 2>圆: 属性:圆心,半径 方法:设置圆心和半径 判断点和圆的关系
#ifndef _CIRCLE_H_ #define _CIRCLE_H_ class Point { private: int m_x; int m_y; public: void SetXY(int x,int y); int Distance(Point &p); }; class Circle { private: Point m_center; //圆心 int m_r; public: void setC(int x,int y,int r); bool Judge(Point &p); }; #endif
#include "circle.h" int main(int argc, char const *argv[]) { Point p; p.SetXY(0,3); Circle c1; c1.setC(0,0,3); if(c1.Judge(p)) { cout<<"点在圆外或者圆上"<<endl; } else { cout<<"点在圆内"<<endl; } return 0; }
#include "circle.h" void Point::SetXY(int x,int y) { this->m_x = x; this->m_y = y; } int Point::Distance(Point &p) { int dis = (p.m_x - m_x)*(p.m_x - m_x) + (p.m_y - m_y)*(p.m_y - m_y); return dis; } void Circle::setC(int x,int y,int r) { m_center.SetXY(x,y); m_r = r; } bool Circle::Judge(Point &p) { if(p.Distance(m_center) >= m_r*m_r) { return true; } else { return false; } }
8.6 对象的构造和析构
(1)构造函数
在c++中,有一种特殊的成员函数,名字和类名相同,没有返回值,不需要用户显示调用(也不能调用),而是在创建对象的时候自动执行. 几点说明: 1.构造函数的名字和类型必须相同 2.构造函数不能有返回值,不能有return语句 3.创建对象的时候自动执行,不能手动调用 4.主要用于对类的成员进行赋初值。(和初始化有区别)
#include <iostream> using namespace std; class Array { private: int size; //数组的容量 int *data; //数组首地址 public: Array(); //无参构造函数 void setVal(int Index,int value); int GetVal(int Index); ~Array(); }; Array::Array() { cout<<"Array的无参构造函数"<<endl; size = 5; data = (int *)malloc(sizeof(int)*size); } void Array::setVal(int Index,int value) { data[Index] = value; } int Array::GetVal(int Index) { return data[Index]; } Array::~Array() { cout<<"Array的析构函数"<<endl; if(data != NULL) { free(data); data = NULL; } } int main(int argc, char const *argv[]) { Array a1; //创建对象,自动调用构造函数 for(int i = 0 ; i < 5;i++) { a1.setVal(i,i+1); } for(int i = 0; i < 5;i++) { cout<<a1.GetVal(i)<<" "; } cout<<endl; return 0; }