类基本
类成员函数
在c++中,类成员函数一般在类内进行定义,但是也可以通过类内声明,在外部利用
范围解析运算符::来定义(对于一些大型的类,类当中只做声明,这样对类的成员以及结构就会显得非常清晰)。
#include <iostream>
#include <string.h>
using namespace std;
class Student
{
public:
int age;
char name[20];
char* setName(char* Name)
{
strcpy(name,Name);
return name;
}
char* getName(void)
{
return name;
}
int setAge(int Age);
int getAge(void);
};
int Student::setAge(int Age)
{
age=Age;
return age;
}
int Student::getAge(void)
{
return age;
}
int main(void)
{
Student stu;
stu.setName("liuxin");
stu.setAge(22);
cout << stu.getName()<<endl;
cout << stu.getAge() <<endl;
}
类访问修饰符
C++的访问修饰符包含:public,protected,private,私密性逐次降低,对于成员变量和成员函数,默认的访问修饰符是private的。
public没什么好说的,关键就是protected和private。
protected:
保护变量在派生类当中是可以被访问的。一般储存父类与子类的共有属性。
#include <iostream>
#include <string.h>
using namespace std;
enum Sex
{
female,
male
};
//学生类
class Student
{
protected:
int age_; //成员变量的命名规范是以_结尾
string name_;
public:
string setName(string name)
{
name_ = name;
return name_;
}
string getName(void)
{
return name_;
}
int setAge(int Age)
{
age_=Age;
return age_;
}
int getAge(void)
{
return age_;
}
};
//高中生类
class SeniorStudent:public Student
{
protected:
string stu_id_;
public:
string setStuId(Sex sex)
{
stu_id_="2019"+to_string(age_)+to_string(sex);
return stu_id_;
}
string getStuId(void)
{
return stu_id_;
}
};
int main(void)
{
SeniorStudent senior_stu;
senior_stu.setAge(17);
senior_stu.setName("liuxin");
Sex sex=male;
senior_stu.setStuId(sex);
cout << senior_stu.getStuId() << endl;
//不能访问protected变量
//cout << senior_stu.stu_id_ <<endl;
}
private:
实际操作中,我们一般会在私有区域定义数据,在公有区域定义相关的函数,以便在类的外部也可以调用这些函数。
————————分割—————————
在继承方面,访问修饰符同样也对类的继承起限定作用,例如public继承,子类对父类的所有访问效果均按照父类本身的修饰符原则,比如父类的成员的修饰符为public,者对于子类也是public,如果是protected就是protected,private一样。但是如果protected继承,则原本的public受限于protected,对于子类,也只能以protected访问。
构造函数与析构函数
构造函数
类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。
注意:对于构造函数来说,访问修饰符也有非常大的作用:
-
通常我们使用public来进行修饰构造函数。
-
protected的构造函数可用于只能在子类中创建父类对象,不能在外面创建父类对象的程序中(将子类的构造函数设置为public,将父类设置为protected)。
-
private的构造函数只能在类的成员函数访问,这种一般用于单例模式,由一个静态的成员函数(可在不创建实例的情况下进行调用。)进行对象的创建,比如想数据库的编程,我们希望只创建一个实例,不希望多个实例对数据库进行访问。
单例模式举例:
#include <iostream> #include <string.h> using namespace std; enum Sex { female, male }; //学生类 class Student { protected: int age_; //成员变量的命名规范是以_结尾 string name_; public: string setName(string name) { name_ = name; return name_; } string getName(void) { return name_; } int setAge(int Age) { static int index; age_=Age; return index++; } int getAge(void) { return age_; } }; enum Position{Monitor,TeamLeader}; //干部类 class Officer:public Student { protected: Position position_; Position setPosition(Position position) { position_=position; return position_; } public: Position getPosition() { return position_; } }; //高中生类 class SeniorStudent:public Student { protected: string stu_id_; public: string setStuId(Sex sex) { stu_id_="2019"+to_string(age_)+to_string(sex); return stu_id_; } string getStuId(void) { return stu_id_; } }; //高中班长类,构造函数是private,对象是静态的,单例模式 class SeniorMonitor:public SeniorStudent,public Officer { private: SeniorMonitor(); public: static SeniorMonitor& Instance() { static SeniorMonitor senior_monitor;//由于单例模式,那么只能创建一次用静态代替,同时静态类型才能以引用返回。 return senior_monitor; } }; SeniorMonitor::SeniorMonitor(void) { Position pos = Monitor; setPosition(pos); } int main(void) { SeniorMonitor& senior_monitor = SeniorMonitor::Instance(); cout << senior_monitor.Officer::setAge(17) << endl; cout << senior_monitor.SeniorStudent::setAge(17)<< endl; //不能访问protected变量 //cout << senior_stu.stu_id_ <<endl; }
析构函数
类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。如果没有手动删除对象,那么就会在作用域结束的时候进行调用。
—————————————割—————————————————————————
注意:派生类会自动调用基类的构造函数和析构函数,调用顺序是:基类带参构造函数,派生类带参构造函数,派生类析构,基类析构。另外,对于new出来的对象,必须要手动调用析构函数,因为new出来的对象都是在堆上开辟了一块内存,直到程序结束,堆上的内存都不会被清空。
拷贝构造函数
拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:
- 通过使用另一个同类型的对象来初始化新创建的对象。
- 复制对象把它作为参数传递给函数。(这种传递方式,为值传递方式,除非有动态资源,否则不会影响原对象)
- 复制对象,并从函数返回这个对象。
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。拷贝构造函数的最常见形式如下:
classname (const classname &obj) {
// 构造函数的主体
}
最普通的情况就是利用一个已经初始化好的对象,去初始化另一个相同类型的对象,如果没有自己定义,工作流程如下所示。
//小组长类,有多个小组长,就不要用单例模式了
class SeniorTeamLeader:public SeniorStudent,public Officer
{
private:
int team_id_;
public:
SeniorTeamLeader(int team_id);
SeniorTeamLeader(const SeniorTeamLeader& obj);
};
SeniorTeamLeader::SeniorTeamLeader(int team_id)
{
//构造函数
Position pos = TeamLeader;
setPosition(pos);
team_id_=team_id;
}
SeniorTeamLeader::SeniorTeamLeader(const SeniorTeamLeader& obj)
{
//构造复制函数
team_id_=obj.team_id_;
cout << "复制构造函数被调用" << endl;
}
void test()
{
SeniorStudent* senior_student =new SeniorStudent();
(*senior_student).getStuId();
}
int main(void)
{
test();
SeniorMonitor& senior_monitor = SeniorMonitor::Instance();
SeniorTeamLeader seninor_team_leader1(1); //这里初始化了一个对象
SeniorTeamLeader seninor_team_leader2 = seninor_team_leader1;//利用初始化后的对象初始化另一个对象。
//不能访问protected变量
//cout << senior_stu.stu_id_ <<endl;
}
注意:在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。下面举个深拷贝的例子。
这种情况下就一定需要自己去定义拷贝函数,手动开辟空间,重新分配资源,由于初学,我这里不再做一个更为详细的记录,等到后面在进行更新。
友元函数
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend,如下所示:
//小组长类,有多个小组长,就不要用单例模式了
class SeniorTeamLeader:public SeniorStudent,public Officer
{
private:
int team_id_;
public:
SeniorTeamLeader(int team_id);
SeniorTeamLeader(const SeniorTeamLeader& obj);
int setTeamId(int team_id);
int getTeamId(void);
//友元函数
friend void test(SeniorTeamLeader obj);
};
SeniorTeamLeader::SeniorTeamLeader(int team_id)
{
//构造函数
Position pos = TeamLeader;
setPosition(pos);
team_id_=team_id;
}
SeniorTeamLeader::SeniorTeamLeader(const SeniorTeamLeader& obj)
{
//构造复制函数
team_id_=obj.team_id_;
cout << "复制构造函数被调用" << endl;
}
int SeniorTeamLeader::setTeamId(int team_id)
{
team_id_=team_id;
}
int SeniorTeamLeader::getTeamId(void)
{
return team_id_;
}
void test(SeniorTeamLeader obj)
{
cout << obj.team_id_ << endl;
}
int main(void)
{
SeniorMonitor& senior_monitor = SeniorMonitor::Instance();
SeniorTeamLeader senior_team_leader1(1);
SeniorTeamLeader senior_team_leader2 = senior_team_leader1;
senior_team_leader2.setTeamId(2);
test(senior_team_leader2);
cout << senior_team_leader1.getTeamId() << endl;
//不能访问protected变量
//cout << senior_stu.stu_id_ <<endl;
}
this
this指针跟python类似,都是指向对象自己的指针,但C++中不需要显示的表现出来。
this指针的使用:
一种情况就是,在类的非静态成员函数中返回类对象本身的时候,直接使用 return *this;另外一种情况是当参数与成员变量名相同时,如this->n = n (不能写成n = n)。
静态成员
静态成员变量
我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。
静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化。
静态成员函数
如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。
注意:静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。
静态成员函数有一个类范围,他们不能访问类的 this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。
静态成员函数与普通成员函数的区别:
- 静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
- 普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针。
我们可以利用静态成员来进行记录某个类的实例化次数。
比如,记录小组长对象有多少个:
//小组长类,有多个小组长,就不要用单例模式了
class SeniorTeamLeader:public SeniorStudent,public Officer
{
private:
int team_id_;
public:
static int objectCount;
SeniorTeamLeader(int team_id);
SeniorTeamLeader(const SeniorTeamLeader& obj);
int setTeamId(int team_id);
int getTeamId(void);
//友元函数
friend void test(SeniorTeamLeader obj);
static int getCount()
{
return objectCount;
}
};
//只能在
int SeniorTeamLeader::objectCount=0;
SeniorTeamLeader::SeniorTeamLeader(int team_id)
{
//构造函数
Position pos = TeamLeader;
setPosition(pos);
team_id_=team_id;
objectCount++;
cout << "构造函数" << endl;
}
SeniorTeamLeader::SeniorTeamLeader(const SeniorTeamLeader& obj)
{
//构造复制函数
team_id_=obj.team_id_;
objectCount++;
cout << "复制构造函数被调用" << endl;
}
int SeniorTeamLeader::setTeamId(int team_id)
{
team_id_=team_id;
}
int SeniorTeamLeader::getTeamId(void)
{
return team_id_;
}
void test(SeniorTeamLeader obj)
{
cout << obj.team_id_ << endl;
}
int main(void)
{
SeniorMonitor& senior_monitor = SeniorMonitor::Instance();
SeniorTeamLeader senior_team_leader1(1);
cout <<"被调用"<< SeniorTeamLeader::getCount() <<"次"<< endl;
//不能访问protected变量
//cout << senior_stu.stu_id_ <<endl;
}
继承
一个派生类继承了所有的基类方法,但下列情况除外:
- 基类的构造函数、析构函数和拷贝构造函数。
- 基类的重载运算符。
- 基类的友元函数。
多继承
继承多个类。
附加要点: