一碗芝士会将自己的学习笔记更新出来,希望大家一起学习。
1、类使用关键词class进行定义,四要素:类名、数据成员、函数成员、访问限定符。
2、访问限定符有:private----私有,public------公有,protected------保护
【注】类的数据类型在默认情况下是private,结构体的数据在默认情况下是public
3、成员函数的定义方式有两种:1)直接在类的内部进行定义 2)在类的内部进行函数的声明,在类的外部进行函数的定义
【注】在外部进行定义时需要使用域作用符 :: ,格式为
返回值类型 类名::函数名(参数);
1、类的定义与普通变量的定义方式相同,即“类名 对象名”,一个对象所占空间大小是类中每一个数据成员所占内存大小之和。
2、类中成员的访问使用" . "运算符(点运算符)。当对象为一个指针时,访问时要使用“->”运算符。
3、两个对象之间可以使用“=”进赋值操作。【注】仅仅使用“=”赋值时需要注意深浅拷贝的问题
(一)静态数据成员
1、静态数据成员使用关键字static,它需要在类内声明,在类外定义或初始化(默认为0),在类创建时没有为静态数据成员分配内存空间,所以需要在类外进行定义。
class Apple
{
int i;
static int n; //static关键字+变量类型+变量名
};
int Apple::n=1; //类外进行定义或初始化,需要使用域作用符
2、静态数据成员不属于类,如果需要更改静态数据成员,则需要调动静态函数成员
(二)静态函数成员
1、静态函数成员与静态数据成员一样不属于类。只能访问静态数据成员。
2、静态函数成员的外部访问:
1)在对象声明之前,静态函数调用需要用到类名和域作用符
类名::静态成员函数
2)在对象声明之后,可以直接通过对象访问静态函数
对象名.静态函数
对象指针->静态函数
(一)构造函数
1、构造函数随着对象的创建而由系统自动调用。构造函数的作用是对类中的数据成员赋初值。
2、构造函数的函数名为类名,构造函数没有返回值类型,且不为void。
类名(参数); //构造函数的结构
3、构造函数根据参数的数目进行重载。
class point
{
private:
int x,y;
public:
point() //没有参数的构造函数
{
x=0;
y=0;
}
point(int i,int j) //由参数的构造函数
{
x=i;
y=j;
}
};
在主函数中调用构造函数时会根据函数的参数列表自动匹配构造函数。
构造函数可以是有参和无参的默认函数
point(int i=0,int j=0); //详见默认函数
4、当一个类中没有构造函数时,系统会自动提供一个缺省版本的构造函数。该构造函数的没有参数,且函数体为空。
5、不同对象的构造函数调用顺序与对象的创建顺序一致。
(二)析构函数
1、析构函数没有参数,没有返回值,且不为void。构造函数在一个类中只能有一个
~类名(); //构造函数规则,无参无返
2、析构函数的调用顺序与构造函数的调用顺序相同,也就是:先创建的对象后析构,后创建的对象先析构
3、如果一个类没有析构函数,系统会自动创建一个缺省版本的析构函数。该析构函数为空函数。所以,当类中有动态内存成员时,一定要自己编写构析构函数,确保动态内存的释放。
//释放动态内存的析构函数写法
class student
{
private:
char* name;
int age;
public:
student()
};
(一)复制构造函数
1、复制构造函数的参数是本类对象的引用
类名(const 类名& 引用名);
使用const的原因是为了防止被引用的对象被改变
2、当类中没有复制构造函数时,系统会提供一个缺省版本的复制构造函数,该函数可以实现简单的不同对象的数据成员的相互赋值。
3、复制构造函数调用的多种情况(重点)
1)用类的一个对象去初始化另一个对象时
2)做值传递时,形参为类对象,调用函数时实参赋值形参。引用传参则不会调用。
3)返回值是对象时,创建一个新的临时对象保留返回值
【注】上述在函数调用时出现复制构造函数调用情况是由于函数调用机制:
(1)当函数只做普通值传递时,会创建一个临时对象用于保存实参,此时会调用复制构造函数;
(2)当函数的返回值为对象时,也会创建一个临时变量用于保存返回对象,此时会调用复制构造函数(函数再调用后即消亡)
当使用引用时则不会出现复制构造函数调用的情况。因此在涉及到类的函数调用中,建议使用引用传参、引用做返回值,可以提高程序运行效率。
#include
#include
using namespace std;
class Student
{
private:
char* name;
int age;
public:
Student(){name=NULL;age=0;};
Student(char* ,int);
Student(const Student& );
~Student();
char* get_name();
int get_age();
};
Student::Student(char* nm,int a)
{
int len=strlen(nm)+1;
name=new char[len];
strcpy(name,nm);
age=a;
cout<<"Student(char*,int)"<<endl;
}
Student::Student(const Student& stu)
{
int len=strlen(stu.name)+1;
name=new char[len];
strcpy(name,stu.name);
age=stu.age;
cout<<"Student(const Student& )"<<endl;
}
Student::~Student()
{
delete []name;
name=NULL;
age=0;
cout<<"~Student()"<<endl;
}
char* Student::get_name()
{
return name;
}
int Student::get_age()
{
return age;
}
//---以上为类的成员函数定义---
char* getName(Student stu)
{
return stu.get_name();
}
int getAge(Student& stu)
{
return stu.get_age();
}
Student compare1(Student stu) //一个测试函数
{
return stu;
}
Student& compare2(Student& stu) //一个测试函数
{
return stu;
}
int main()
{
Student stu1("ZhangSan",18);
Student stu2("zhaoSi",19);
cout<<endl;
Student stu3(stu1); //一个对象初始化另一个对象时会调用复制构造函数
cout<<endl;
cout<<getName(stu3)<<endl<<endl; //当类的对象做函数的值传递时会调用复制构造函数
cout<<getAge(stu2)<<endl<<endl; //当类的对象做函数的引用传参时则不会调用复制构造函数
stu3=compare1(stu2); //当类的对象作函数的返回值时会调用复制构造函数
cout<<endl;
stu3=compare2(stu1); //当类的引用做函数的返回值时则不会调用复制构造函数
cout<<endl;
return 0;
}
(二)深拷贝与浅拷贝(重点&难点&易错点)
深拷贝与浅拷贝的系类问题会发生在涉及到指针和引用的数据赋值当中。
1)浅拷贝出现问题的原因:
直接使用“=”赋值时,或者使用默认复制构造函数进行对象复制时,如果对象中存在指针数据,那么复制的就只是指针的地址,即两个指针都指向同一块地址。
当再对对象进行析构时,会将对象中的指针指向的地址删除(或置空NULL),那么就会出现同一块地址被删除(置空)两次的情况,则计算机系统会出现错误。
即“一个内存被delete了两次”
2)深拷贝
深拷贝可以解决浅拷贝出现的问题。
分析浅拷贝报错的原因:两个指针指向同一个内存。由于析构对象时会将两个指针指向的内存都删除,导致一个内存被删除两次
解决方法:将两个指针指向的变为不同的两个内存,但是两个内存中存储的数据相同,即”开辟不一样的地址存储一样的数据“
//上面代码已展示大部分
class Student
{
private:
char* name;
int age;
public:
Student(){name=NULL;age=0;};
Student(const Student& ); //深拷贝的复制构造函数
~Student();
};
Student::Student(const Student& stu)
{
int len=strlen(stu.name)+1;
name=new char[len];
strcpy(name,stu.name);
//以上三步重新为name开辟了一个内存,存储stu对象的name数据
//浅拷贝:name=stu.name;
age=stu.age;
cout<<"Student(const Student& )"<<endl;
}
Student::~Student()
{
delete []name;
name=NULL;
age=0;
cout<<"~Student()"<<endl;
}
以上为个人学习笔记,如有错误请指正!!
谢谢大家~