引用一个变量就是定义了一个变量,和原来的变量使用同一个值。引用变量将值改变,原来这个变量的值也随之改变。 它和传地址的性质一致。
/**
*定义一个引用类型的变量b,引用了变量a
*/
int a;
int &b = a;
/**
*声明函数xxx
*/
void xxx(int &);
/**
*实现函数xxx
*/
void xxx(int &x){
x++;
}
/**
*执行函数xxx
*/
int main(){
int a = 20;
xxx(a);//int &x = a;传递引用
cout<<a;//结果为21
}
/**
*声明函数xxx
*/
void xxx(int *);
/**
*实现函数xxx
*/
void xxx(int *x){
*x++;
}
/**
*执行函数xxx
*/
int main(){
int a = 20;
xxx(&a);//传递地址
cout<<a;//结果为21
}
给函数传值就相当于将值拷贝一份给这个函数,这个函数进行操作的这个值和原本的这个值一点关系都没有,因此原本的值不变。
/**
*声明函数xxx
*/
void xxx(int );
/**
*实现函数xxx
*/
void xxx(int x){
x++;
}
/**
*执行函数xxx
*/
int main(){
int a = 20;
xxx(a);//传值
cout<<a;//结果为20
}
/**
*函数的声明
*/
int xxx(int a,double b=0,int c=10);
默认值在声明的时候设置; 默认值可以有可以没有,但有默认值的参数必须出现在右侧。
根据返回值类型的不同,函数可以分成三种类型。
int &fun(){//要将函数名前加上&
static int n;
n++;
return n;//返回一个引用
}
将被调用函数的函数体直接嵌入在被调用处,这种函数叫做內联函数。 在函数的最前面加上inline就是內联函数了。 內联函数的函数体内不能有循环、switch、复杂的if-else。
/**
*只要在函数定义的时候进行以下操作,函数调用的时候照常使用即可
*/
template <typename T>
T add(T x,T y){
return x+y;
}
函数也是有地址的,因为函数的代码也需要放在一片连续的存储空间中。而函数的地址就是函数名。
函数指针的声明:
//已有函数声明
double fun(int,int,double);
//声明函数指针p,并将p指向fun的代码区域
double (*p)(int,int,double)=fun;
此时,用p和用fun效果是一模一样的。
define指令又叫做宏指令,遇到宏名,用宏内容原封不动地替换宏名。宏内容中可以包含标点符号。 当宏名出现在字符串中不进行替换。 “undef 宏名“表示宏替换在此处终止。
/**
*如果定义了xx,就编译……,否则就编译……
*/
#ifdef/#ifndef xx
……
else
……
#endif
条件编译可以防止头文件地嵌套包含,从而避免重复编译。
#ifndef HD
#define HD
……
#endif
成员函数可以在类内只写函数原型,而定义写在类的外边。当函数的定义写在类的外边时,要在函数名的前面加上类名:void 类名::函数名(参数){ 函数实现 }
一个源程序要分成三个文件,一个.h文件,存放类的声明;一个.cpp文件,存放类的实现;一个.cpp文件,存放类的使用。
//与java中不同,c++中只要这么写,对象就已经创建好了。
Student stu1,stu2;
//没有使用new创建的对象之间可以整体赋值
stu1 = stu2;//这样就是把stu2各个成员变量的值一一复制给了stu1
对象调用成员的两种方式
//对象调用成员,通过.来调用
stu1.getName();
stu1.name
//对象的地址通过->来调用
&stu1->getName();
&stu1.name;
this指针 每个成员函数的行参表中,都隐藏着代表本类对象的this指针
void Student::setName(string name,Student *this){
this->name = name;
}
使用new创建对象
Student *stu = new Student();
//使用new创建的是对象的指针,必须在对象前加上*
//使用new会调用相应的构造函数,也可以没有括号表示调用空参构造器。
析构函数 析构函数是用来完成对象在生命周期结束前释放内存空间用的。在对象的生命周期结束前会由系统自动调用。 析构函数的特点:
#include<iostream>
using namespace std;
class Student{
private:
char *name;
public:
Student(char *name);
~Student();
void show(){
cout<<"name:"<<name<<endl;
}
}
Student::Student(char *name){
if(name){
name = new char[strlen(name)+1];//创建一个char类型的数组,长度是name的长度+1
this->name = name;
}
this->show();
}
~Student::Student(){
if(this->name)
delete []name;//释放数据name的空间
}
int main(){
Student stu1("chai");//这是Student stu1 = new Student("chai")的简写形式
Student stu2("zhang");
return 0;
}//当执行到这里的时候,系统会自动调用两次析构函数,将stu1、stu2的空间释放掉。
Student s1;//已经创建了对象s1
Student s2(s1);//将s1浅拷贝给s2
Student s2 = s1;//与上面等价
浅拷贝是将s2的指针指向s1所在的区域,从而s1与s2共享一块区域。 浅拷贝会存在一个问题,由于拷贝前后的两个对象同时指向同一块内存空间,所以当析构这两个对象的时候,同一片空间会被析构两次,如果第一次将存储空间释放后,在第二次析构之前,这块空间又已经存放了其他数据,此时第二次析构了这块空间, 所以将原本不需要析构的空间释放掉了,因此产生了错误。
//自定义的拷贝构造函数
Student::Student(Student &stu){
if(stu.name){
(this->)name = new char[strlen(name)+1];
strcpy(name,stu.name);//是否等价于this->name = name?
}
}
int main(){
Student stu1;
stu1.setName("chai");
//调用拷贝构造函数
Student stu2(stu1);
return 0;
}
//静态成员变量在类内的引用性声明
static 类型名 成员变量;
//静态成员变量在类的定义之外,在文件作用域的某处作正式声明,即进行初始化
类型 类名::变量名 //默认值为0
类型 类名::变量名=xxx;//给静态变量指定初始化值
//静态成员变量使用的两种方法(和java一样)
//假设a是一个静态成员变量
Student stu;
stu.a;//第一种使用方法,用对象调用;
Student::a;//第二种使用方法,用类调用;