1、类(class)
描述现实世界中的事物,用C++进行编程的时候,通常是将现实世界中的事物用类来进行描述。不是真实存在的,是一种概念。。
用类描述事物的格式:
class 类名(标识符名称)
{
//特征—变量
//行为—函数
};
2、对象
对象是类的实例化(特指实际的例子),将脑海中的概念实际的映照在现实事务中。如:交通工具——实例化——车。
对象的实例化格式:
(1)在栈区实例化——栈区实例化的对象,不需要用户手动的去释放内存,编译器会自动释放内存
格式:
类名 对象名(标识符名称);
(2)在堆区实例化-------堆区实例化的对象,需要用户手动的去释放内存,如果不释放,很容易造成内存泄露
格式:
类名 * 对象名(标识符名称)= new 类名;
(3)如何访问类里面的成员(成员变量+成员函数)
1)、在栈区实例化
访问成员格式:
对象名(标识符名称). 成员;
2)、在堆区实例化
访问成员格式:
对象名(标识符名称)-> 成员;
3、类的成员访问修饰符
类成员访问权限:
(1)public(公共)
整个类:可见
类外:可见
友元函数:可见
派生类(子类):可见
(2)private(私有)
整个类:可见
类外:不可见
友元函数:可见
派生类(子类):不可见
(3)protected(受保护)
整个类:可见
类外:不可见
友元函数:可见
派生类(子类):可见
作用:为了保护类的成员
访问修饰符的作用范围:
从修饰符:开始,一直到“}”(类结束)或下一个修饰符。
注:
(1)、如果在类里面没有写任何的类的成员访问修饰符,那么该类下的成员都是归于private属性下。
(2)、一般将成员变量放置到private属性下
4、构造函数
构造函数是一种特殊的函数,用户可自己编写,如果用户没有显示的书写构造函数,那么编译器会帮用户自动的生成了一个默认的构造函数,可存在多个(一般用于初始化变量)
对象创建时,构造函数会自己调用
构造函数格式:
格式1:
类名 (参数列表)
{
//代码块—用于初始化类的成员变量
}
格式2:
类名 (参数列表):成员变量1(初始值1),成员变量2(初始值2),…
{
//代码块
}
总结:构造函数与普通函数的区别
不同点:
1、普通函数前有返回值类型,而构造函数前没有返回值
2、普通函数后面没有初始化列表,而构造函数后有初始化列表
3、普通函数调用的时候以显式调用的方式,而构造函数不仅可以显示的调用还可以隐式的调用
相同点:
1、都可以带参数或者是不带参数
2、都可以发生重载
5、析构函数
C++里的析构函数也是一种特殊的函数,用户可自己编写,如果用户没有显示的书写析构函数,那么编译器会自动帮用户生成一个默认的隐藏的析构函数,且只可存在一个(一般用于释放空间)
对象消亡时自动调用
析构函数格式:
格式:
~类名 ()
{
//代码块
}
6、拷贝构造函数
拷贝------copy,如果用户需要用一个对象去初始化另外一个对象的时候,就会用到拷贝构造函数,其实质就是一个构造函数,如果用户没有显示的将拷贝构造函数写出来,那么编译器会自动的生成一个默认的隐藏的拷贝构造函数,反之,编译器就不会帮用户生成一个拷贝构造函数
拷贝构造函数的格式:
类名(const 类名 & obj)
{
//拷贝构造函数代码块
}
(1)浅拷贝
相当于是把一个对象里的成员变量和它的值拷贝了一份儿给另外一个对象。
如果类里面有指针对象并且在堆区开辟了空间,而且在析构函数里对指针对象进行了释放,这时候如果使用浅拷贝,造成堆区空间重复释放,而引起程序崩溃
(2)深拷贝
相当于是把一个对象里的成员变量拷贝了一份儿,但是变量的地址又重新开辟了一个空间,解决的问题就是堆区重复释放造成程序崩溃的问题
注:
1、析构函数不能带参数
2、类体里面只允许有一个析构函数(析构函数不能发生重载)
3、析构函数前没有返回值
7、this指针
它是一种特殊的指针,它是存在于类的普通成员函数里,C++提供this指针的目的是为了解决二义性问题,并且它是隐式的指针,它是指向当前类。
原型:
成员函数(类名 * const register this)
使用:
this->成员
注:
1、静态成员函数中没有this指针
2、友元函数中没有this指针
8、static
用static声明函数或者是变量为静态的
(1)静态成员函数
格式:
static 返回值类型 函数名(参数列表)
{
//函数体
}
静态成员函数的访问格式:
a、通过类的对象访问类的静态成员
格式:
对象名.静态成员;
对象名->静态成员;
b、通过类加上域作用符号来访问类的静态成员
格式:
类名 ::静态成员;
注意:
1、静态成员函数里没有this指针
2、静态成员函数不能访问类的非静态成员
3、类的非静态成员函数可以访问类的静态成员
(2)静态成员变量
格式:
static 数据类型 变量名;
注:
在类里面不能对类的静态成员变量进行初始化,需要在类外对静态成员变量进行初始化
类外初始化的格式:
数据类型 类名 ::静态成员变量名 = 初始值;
9、const
(1)const修饰成员变量
作用与C类似,有区别的是在类里声明的const成员变量可以不用声明的时候进行初始化,如果没有进行初始化就必须在构造函数的初始化列表中进行
初始化的工作
(2)const修饰的成员函数
const修饰的成员函数中不能去更改类的成员变量的值(只可读)
格式:
返回值类型 函数名 (参数列表)const
{
//函数体
}
10、友元
目的:给类的外部提供一个访问类的私有成员的接口 。友元函数声明在类里,就表示该函数是该类的友元,那么就可以通过友元函数访问类的私有成员,友元函数的定义则放置在类的外部。
关键字:friend
(1)友元函数
友元函数声明在类里,就表示该函数是该类的友元,那么就可以通过友元函数访问类的私有成员,友元函数的定义则放置在类的外部。
friend 返回值类型 函数名(参数列表);//友元函数的声明
定义(在类外定义):
返回值类型 函数名(参数列表)
{
//函数体
}
注:
1、友元函数虽然声明放置在类的内部,但是它不属于类的成员函数
2、友元函数没有this指针
(2)友元类
友元类声明在一个其他类里,就表示该友元类是其他类的友元,那么就可以通过友元类访问其他类的私有成员,友元类的定义放置在其他类的外部。
声明格式:
friend class 类名;//声明了一个友元类
定义(在其他类外定义):
class Test
{
//类体
}
11、请教的问题
程序
#include"iostream"
using namespace std;
class person
{
public:
char* name;
int age;
char * sex;
person()
{
name = nullptr;
age = 0;
sex = nullptr;
cout << "无参" << endl;
}
person(const char* n,int a,const char *s)
{
char* p= new char[strlen(n) + 1];
name = p;
strcpy_s(name, strlen(n) + 1, n);
age = a;
sex = (char*)s;
cout << "有参 " << endl;
}
void set(const char* n, int a, const char* s)
{
char * p = new char[strlen(n) + 1];
name = p;
name = (char *)n;
age = a;
sex = (char*)s;
cout << "输入 " << endl;
}
person(const person& obj)
{
char *p = new char[strlen(obj.name) + 1];
name = p;
name = obj.name;
age = obj.age;
sex = obj.sex;
cout << "拷贝 " << endl;
}
~person()
{
delete [] name;
cout << "析构" << endl;
}
void print()
{
cout << "name:" << name << " age:" << age << " sex:" << sex << endl;
cout << "name 地址:" << &name << endl;
}
};
int main()
{
person p1;
p1.set("花花", 12, "男");
cout << "P1" << endl;
p1.print();
person p2("菲菲", 12, "女");
cout << "P2" << endl;
p2.print();
person p3(p2);
cout << "P3" << endl;
p3.print();
return 0;
}
执行结果