C++
C++作为面向对象的一种语言,可以把一切具有相同特性的客观事物抽象出形成类,所以类也是c++语言中非常重要的一环。
C++类的定义
所有类的定义都是class+类名,后面跟{};一个类里面应该有的成员:
1.三种访问修饰符:private(私有成员),public(公有成员),protect(保护成员);
2.构造函数,析构函数,普通成员函数;
3.成员变量。
所有的成员变量和成员函数都是用来起到修饰对象的作用,即都是对象的属性。
接下来是一段定义的代码:
class SStudent{
private:
char name[20];
int age;
int ID;
};
这是一个学生类的定义,私有化内的三个成员变量皆为学生的属性。
类的对象&成员函数
对象
对象的定义形式为:类名 对象名;(与变量的定义有些类似)
对于一般的对象来说,每个对象都有自己的存储空间,一个对象内的成员变量被改变并不会影响其他对象内的成员。
一个对象的大小=对象内所有成员变量大小之和。
class SStudent{
private:
char name[20];
int age;
int ID;
};
int main()
{
SStudent student;
return 0
}
sizeof(student)=sizeof(name)+sizeof(age)+sizeof(ID)=9
类访问成员变量成员函数有三种方法:
1、对象名.成员名
class SStudent{
private:
char name[20];
int age;
int ID;
public:
int output()
{
cout<<"HelloWord!"<<endl;
}
};
int main()
{
SStudent student;
student.age=18;
student.output;
cout<<student.age<<endl;
return 0
}
运行结果
HelloWord!
18
2、指针->成员名
SStudent student1,student2;///定义SStudent类的两个对象
SStudent *p1=&student1;
SStudent *p2=&student2;
p1->age=20;//对象studentd1的年龄被修改为20
p2->ID=110110;//对象student2的学号被修改为110110
3、引用名.成员名
SStudent student;
SStudent s&student;
s.age=18;//对象student的年龄被修改为18
引用就相当于该对象的一个别名,并不会开辟新的内存空间,而是与对象共用一个内存空间。这里不做过多详解。
成员函数
本版块成员函数为普通成员函数,不进行过多分类。
成员函数的声明定义和调用
1、成员函数的定义与c语言的普通函数定义无异,都是类型名+函数名(类型名 形参名,类型名 形参名...){}。
其中,声明时可以省略形参名,定义不可省略。
2、成员函数的调用与对象调用方式相同
对象名.成员函数名
指针名->成员函数名
引用名.成员函数名
内联函数和重载成员函数
内联成员函数
1、成员函数整体的声明与定义都在类的内部
class SStuednt{
private:
int age;
public:
void func(int i)
{
age=i;
cout<<"run successfuly!"<<endl;
}
};
2、在类内进行声明,类外进行定义(声明时需要在类前加上inline,定义时在函数名前加上类名)
class SStuednt{
private:
int age;
public:
inline void func(int i);
};
void SStudent::func(int i)
{
age=i;
cout<<"run successfuly!"<<endl;
}
以上为定义内联函数的两种方式,内联函数的作用就是可以解决函数频繁调用的问题。使用内联函数,在编译器编译时就会把程序中调用内联函数的部分换成内联函数结构体,而其他函数只有在运行时才会替转。
重载成员函数
重载需满足两点:
1、函数名相同
2、形参个数不同/形参类型不同/形参类型顺序不同
通过以下例子可以更直观了解
#include<iostream>
using namespace std;
class SStuednt{
private:
int age;
int ID;
public:
void func(int i,int j){age=i;ID=j;};
void func(int i,int j=110110){age=i;ID=j}
int func(){return age};
};
int main()
{
SStudent student;
student.func(18,110111);
student.func(23);
cout<<student.func()<<student.ID<<endl;
return 0;
}
运行结果
23 110110
当调用重载函数时,系统会自动根据实参的类型个数来匹配相应的重载函数。
构造函数
构造函数的作用是对对象进行初始化,给成员变量赋初值;
名字与类名相同,一个类只能有一个构造函数,可以有参数但是不能有返回,构造函数可以重载;
如果定义类时不写构造函数的话,系统会自动生成一个默认构造函数,默认构造函数无参且什么也不做。
形式为 :类名()
class SStuednt{
private:
int age;
int ID;
public:
SStudent(){}//默认构造函数,什么也不做
SStuednt(int i,int j){age=i;ID=j; cout<<"succseefuly!"<<endl;}//自己创建的构造函数,用来初始化成员变量
SStudent(int i,int j):age(i),ID(j)//初始化的另一种方式,初始化列表
};
int main()
{
SStudent student1;//无参会调用默认构造函数进行初始化,初始值随机
SStudent student2(18,110110);//调用第二个函数进行初始化
return 0;
}
初始化列表的使用方式:
类内:构造函数名(参数表):成员变量1(参数表),成员变量2(参数表),...
类外: 类名::构造函数名(参数表):成员变量1(参数表),成员变量2(参数表),...
析构函数
析构函数名也与类名相同,但是没有参数也没有返回值,一个类只能有一个析构函数;
析构函数的命名规则是:~ 类名
析构函数在对象消亡时自动被调用,主要做对象消亡前的善后工作,如释放内存;
如果类内不进行析构函数的定义,系统则会自动生成一个缺省析构函数;
析构函数秉承先被构造的对象后被析构(作用域不同的对象除外)
通过以下代码进行进一步理解:
#include<iostream>
using namespace std;
class Demo{
int id;
public:
Demo(int i)
{
id=i;
cout<<"id="<<id<<"Constructed"<<endl;//在执行构造函数时就会打印id的值和constructde
}
~Demo()//在执行析构函数时就会打印输出当前id的值和后面这句话
{
cout<<"id="<<id<<"Deestructed"<<endl;
}
};
Demo d1(1);
void Func(){
static Demo d2(2);
Demo d3(3);
cout<<"Func"<<endl;
}
int main()
{
Demo d4(4);
d4=6;
cout<<"main"<<endl;
{ Demo d5(5); }
Func();
cout<<"main ends"<<endl;
return 0;
}
运行结果:
id=1Constructed
id=4Constructed
id=6Constructed
id=6Deestructed
main
id=5Constructed
id=5Deestructed
id=2Constructed
id=3Constructed
Func
id=3Deestructed
main ends
id=6Deestructed
id=2Deestructed
id=1Deestructed
复制构造函数
复制构造函数,也叫拷贝构造函数;
命名形式如下 类名(const 类名 &对象名)
SStudent(const SStudent &student )
复制构造函数起作用的三种情况:
1、一个对象被同类的另一个对象初始化时
#include<iostream>
using namespace std;
class SStuednt{
private:
int age;
int ID;
public:
void func(int i,int j){age=i;ID=j;}
int func(){return age}
~func(){}
};
int main()
{
SStudent student1(18,110110);
SStudent student2(student1);
cout<<student2.age<<student.ID<<endl;
return 0;
}
运行结果
18 110110
2、某函数的一个参数为类A的对象,当该函数被调用时
class A{
private:
int a;
public:
A(){}//默认构造函数
~A(){}//默认析构函数
};
void func(A a1){}
int main()
{
A a2;
func(a2);
return 0;
}
3、某函数的返回值为类A的对象,当函数值返回时,调用复制构造函数
class A{
private:
int a;
public:
A(int i){a=i;}
~A(){}//默认析构函数
A(const A&n){a=n.a}
};
void A func()
{
A b(3);
return b;
}
int main()
{
cout<<func().a<<endl;
}
运行结果
3
静态成员和静态成员函数
在成员变量前加上static定义为静态成员变量
静态成员在类的所有对象中是共享的,即不存在单独的对象,只有共同的对象。
sizeof运算符计算大小时不会计算静态成员的大小
class SStudent{
int a;
int static b;
};
此时sizeof(SStudent)=4
访问静态成员有三种方法:
类名::成员名 SStudent::b
对象名.成员名 student.b
指针->成员名 p->b
在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数。
友元
友元函数
友元函数的作用:一个类的友元函数可以访问该类的私有成员
通过一段代码进行深入认识
#include<iostream>
using namespace std;
class CCar;
class CDriver{
public:
void ModifyCar(CCar*pCar);
};
class CCar{
private:
int price;
friend int MostExpensiveCar(CCar cars[],int total);
friend void CDriver::ModifyCar(CCar *pCar);
};
void CDriver::ModifyCar(CCar*pCar)
{
pCar->price+=1000;
}
int MostExpensiveCar(CCar cars[],int total)
{
int tempMax=-1;
for(int i=0;i<total;++i)
if(cars[i].price>tempMax)
tempMax=cars[i].price;
return tempMax;
}
int main()
{
return 0;
}
友元类
与友元函数作用类似
A是B的友元类,A的成员函数就可以访问B的私有成员
class B{
private:
int a,b;
public:
B func1();
friend class A;
};
class A{
plublic:
int c;
func2(){c+=a+b}
};
int main(){
return 0;
}
友元类不能传递,不能继承。
this指针
在同一个类中,每个对象都可以通过this指针来访问自己的地址,作用就是指向成员函数所作用的对象。
this的特性:
1. this指针的类型:类名* const。
2. 只能在“成员函数”的内部使用。
3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
#include<iostream>
using namespace std;
class SStudent{
private:
int age;
int ID;
public:
void func(){cout<<age<<" "<<ID<<endl;}
SStudent(int _age,int _ID){age=_age,ID=_ID;}
};
int main()
{
SStudent s1(18,110110);
SStudent s2(20,110111);
s1.func();
s2.func();
return 0;
}
运行结果
18 110110
20 110111
上面代码如果写出隐藏的this指针如下,方便大家理解
#include<iostream>
using namespace std;
class SStudent{
private:
int age;
int ID;
public:
void func(SStudent const *this){cout<<this->age<<" "<<this->ID<<endl;}
SStudent(SStudent *this int _age,int _ID){age=_age,ID=_ID;}
};
int main()
{
SStudent s1(18,110110);
SStudent s2(20,110111);
s1.func(&s1);
s2.func(&s2);
return 0;
}
可以看出this指针是const型,不可被更改。