目录
前言
C++中的类和对象。
一、类的定义
1.类的定义
类的定义格式如下:
class 类名{
public:
<公有的数据和函数>
private:
<公有的数据和函数>
protected:
<公有的数据和函数>
}
例如我们定义一个xue'sheng'lei
class Student {
private:
int no;//学号
string name;//姓名
char sex;//性别
string homeTown;//j家乡
public:
void show(){
cout<<"no:"<<no<<endl;
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"homeTown:"<<homeTown<<endl;
}
};
2.类成员的访问控制
类中的成员具有不同的访问权限修饰符。从访问权限上讲,类的成员又分为public、private、protected三类:
1.public: 定义外部接口,只有公有成员可以访问呢
2.private:定义类的内部数据和函数,只能被自己所属类和友元函数访问
3.protected:在类的继承和派生中使用
3.类的数据成员
类的数据成员用来描述类的属性。
4.类的成员函数
类的成员函数用来实现类中数据成员的操作,描述了类的行为。
成员函数既可以在类的内部定义,也可以在类的外部定义。
1.内联函数
在类的内部直接给出定义的函数,称之为内联函数。
例如我们在Student中定义一个show函数,用来打印学生类中学生的信息。
class Student {
private:
int no;//学号
string name;//姓名
char sex;//性别
string homeTown;//j家乡
protected:
int variable1;
public:
void show(){
cout<<"no:"<<no<<endl;
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"homeTown:"<<homeTown<<endl;
}
};
2.在类的外部定义函数
我们也可以在类的外部定义函数.
例如我们定义了一个display函数,函数的实现是在类的外部实现的,函数的定义是在类的内部。我们在类的内部定义函数,在类的外部实现函数的时候,如果我们都加上关键字inline,那么这个函数就可以被系统当做内联函数处理。
class Student {
private:
int no;//学号
string name;//姓名
char sex;//性别
string homeTown;//j家乡
protected:
int variable1;
public:
void show(){
cout<<"no:"<<no<<endl;
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"homeTown:"<<homeTown<<endl;
}
void display(int,string,char,string);
};
void Student::display(int no, string name, char sex,string homeTown){
cout<<"no:"<<no<<endl;
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
cout<<"homeTown:"<<homeTown<<endl;
}
二、对象的定义
对象是类的实例,一个对象必须属于一个已知的类。
1.对象的定义
对象的定义格式如下:
类名对象名(参数列表);
除了可以定义一般类对象之外,还可以定义对象数组、指向对象的指针变量或引用。例如我们有一个已经定义好的Shape类,定义一般类对象、对象数组、对象指针和对象引用,实例代码如下:
Shape s1,s2;
Shape * s3;
Shape & shape = s1;
2.对象的成员
一个对象的成员就是该对象的类所定义的成员,包括数据成员和成员函数。
1.使用"."访问对象的成员
#include <iostream>
using namespace std;
class Time {
public:
int hours;
int minutes;
int seconds;
void showTimes(int hours,int minutes,int seconds){
this->hours = hours;
this->minutes = minutes;
this->seconds = seconds;
cout<<hours<<":"<<minutes<<":"<<seconds<<endl;
}
};
int main(int argc, const char * argv[]) {
Time timer1;
timer1.showTimes(10, 10, 10);
timer1.hours;
timer1.minutes;
timer1.seconds;
return 0;
}
2.使用"."运算符访问对象引用和对象数组的成员
在上面的例子中,我们还可以通过&访问对象的成员。
Time & time2 = timer1;
time2.hours;
time2.minutes;
3.使用"->"运算符访问一个指针变量所指向的对象成员
我们可以通过下面格式访问对象成员。
对象指针名->成员名;
上述所定义的Time类中,我们可以通过下面指针的方式访问对象。
Time time1;
time1.showTimes(10, 10, 10);
Time * time3 =&time1;
cout<<time3->hours<<":"<<time3->minutes<<":"<<time3->seconds<<endl;
三、构造函数和析构函数
类是一种用户自定义类型,声明一个类对象的时候,编译程序要为对象分配存储空间,进行必要的初始化。在C++中,对象的初始化是通过构造函数类实现的,当撤销对象的时候,析构函数负责回收存储空间,并且做一些善后的工作。析构函数和构造函数都属于类,接口可以由用户提供,也可以由系统自动生成。
1.构造函数和析构函数的定义
构造函数用于在创建对象的时候利用特定的值构造对象,将对象初始化为一个特定的状态。
析构函数用于在销毁对象的时候,做一些清理工作,完成释放一个对象的功能的成员函数。
1.构造函数
构造函数是特殊的成员函数,函数体可以写在类体内,也可以写在类体外。构造函数的名字和类名相同,该函数不能指定返回类型。
还以上面的Time类为例,我们定义个构造函数来初始化Time类的初始属性。
class Time {
public:
int hours;
int minutes;
int seconds;
Time(){
this->hours = 0;
this->minutes = 0;
this->seconds = 0;
}
};
和其它普通的类一样,我们也可以在类的外部定义该构造函数。
class Time {
public:
int hours;
int minutes;
int seconds;
Time();
};
Time::Time(){
this->hours = 0;
this->minutes = 0;
this->seconds = 0;
}
构造函数可以重载,我们可以定义其它的析构函数。
class Time {
public:
int hours;
int minutes;
int seconds;
Time();
Time(int hour){
this->hours = hour;
}
Time(int hour,int minutes){
this->hours = hour;
this->minutes = minutes;
}
Time(int hour,int minutes,int seconds){
this->hours = hour;
this->minutes = minutes;
this->seconds = seconds;
}
};
构造函数可以被系统调用,但是不能直接调用析构函数。
构造函数的调用方式为:
类名 对象名(参数列表);
还以上面的代码为例,我们调用构造函数的代码如下:
Time time();
Time time1(10);
Time time2(10,20);
Time time3(10,20,30);
构造函数还可以和new运算符一起使用。
Time * time4 = new Time();
2.析构函数
析构函数和构造函数的用法基本相同。
析构函数的名称和类名相同,类面前面加上关键字"~"。析构函数不能重载,也没有参数。
一个类中只能有一个析构函数。
析构函数在对象生命周期结束的时候被系统调用,程序中一般不调用析构函数。
在上面的例子中,我们可以定义下面的析构函数:
class Time {
public:
int hours;
int minutes;
int seconds;
~Time(){
std::cout<<"析构函数被调用"<<endl;
}
Time(){
std::cout<<"构造函数函数被调用"<<endl;
}
};
2.默认构造函数和析构函数
当我们没有定义构造函数和析构函数的时候,系统默认会声明一个不带参数的构造函数和析构函数。还以上面的Time类为例,默认构造函数为
~Time(){
}
Time(){
}
3.拷贝构造函数
拷贝构造函数是一个特殊的构造函数,具有一般构造函数的所有特性,它只有一个参数,参数类型是本类对象的引用,用一个已知的对象来初始化一个呗创建爱的同类的对象。如果定义了拷贝构造函数,则在用一个类对象初始化该类另一个对象时它就会被调用。拷贝构造函数的格式为:
类名::拷贝构造函数(const 类名&引用对象名){
}
三种情况下拷贝构造函数会被自动调用:
1.用类的已知对象定义该类的一个正在被创建的对象
2.对象做为函数的实参传递给函数形参
3.对象作为函数返回值
四、自由存储对象
自由存储对象是在程序运行过程中根据需要随时建立和删除的对象。用堆运算符new和delete来完成建立和删除的过程。
new创建的对象,根据参数调用相应的构造函数;用new创建对象数组的时候,会调用默认构造函数;用delete删除对象的时候,调用析构函数。
五、this指针
this指针是成员函数所属的对象指针,它指向类对象的地址。成员函数通过这个指针可以知道自己属于哪一个对象。
当一个对象创建爱你完成之后,它的每一个成员函数都含有一个系统自动生成的隐含指针-this指针,用以保存这个对象的地址。因此this也被称为指向本对象的指针。this指针存取类成员变量的方式为:
this->成员变量;
六、静态成员
类的静态成员是解决同一个类的不同对象之间的数据和函数共享问题的。不管这个类创建了多少个对象,它的静态成员都只有一个副本,这个副本被所有属于这个类的对象共享。
1.静态数据成员
类的静态数据成员是类的所有对象的共享成员,而不是某个对象的成员。
静态数据成员在定义或者说明的时候前面加上关键字static。
静态数据成员必须进行初始化,而且它的初始化与一般的数据成员初始化不同。它的初始化格式为:
数据类型 类名::静态数据成员名 = 值;
例如在下面的代码中,我们定义了一个ClassA类,类中有一个静态数据variable2,定义静态数据成员和赋值静态数据成员代码如下:
class ClassA {
public:
int variable;
static int variable2;
};
int ClassA::variable2 = 100;
访问静态数据成员格式如下:
类名::静态数据成员
在上面定义的ClassA类,我们可以通过下面的代码访问:
#include <iostream>
using namespace std;
class ClassA {
public:
int variable;
static int variable2;
};
int ClassA::variable2 = 100;
int main(int argc, const char * argv[]) {
cout<<"Class.variable2:"<<ClassA::variable2<<endl;
return 0;
}
2.静态成员函数
静态成员函数也是在函数名称前面加上关键字static。
调用静态成员函数的形式为:
类名::静态成员函数名(参数列表);
或者
类名::静态成员函数名();
实例代码如下:
#include <iostream>
using namespace std;
class ClassA {
public:
static void show(){
cout<<"调用静态成员函数show"<<endl;
}
};
int main(int argc, const char * argv[]) {
ClassA::show();
return 0;
}
当然我们可以通过对象名来访问静态函数。
#include <iostream>
using namespace std;
class ClassA {
public:
static void show(){
cout<<"调用静态成员函数show"<<endl;
}
};
int main(int argc, const char * argv[]) {
ClassA classA = ClassA();
classA.show();
return 0;
}
七、常成员
我们对与需要共享的数据定义为常量进行保护,以保证它在整个程序运行期间是不可改变的。这些常量用const修饰符进行定义。const修饰对象、对象的成员函数、数据成员的时候分别称为常对象、常成员函数和常数据成员。
1.常对象
使用const修饰对象称为常对象。
常对象的定义形式为:
类名 const 对象名;
或者
const 类名 对象名;
常对象只能调用常成员函数,不能调用其它的函数。
例如在下面的代码中,常对象constClassA是无法调用show函数的。
#include <iostream>
using namespace std;
class ClassA {
public:
void show(){
cout<<"调用静态成员函数show"<<endl;
}
};
int main(int argc, const char * argv[]) {
const ClassA constClassA = ClassA();
// constClassA.show();
return 0;
}
2.常成员函数
使用const修饰的函数成为常成员函数。
常成员函数格式如下:
返回类型 成员函数名(参数列表) const;
例如在下面的代码中,我们定义了常成员函数constShow.
class ClassA {
public:
void constShow() const{
cout<<"常成员函数"<<endl;
}
};
如果在类外实现常成员函数的时候,也要加上关键字const.
上面的代码也可以写成下面的代码:
class ClassA {
public:
void constShow() const;
};
void ClassA::constShow()const{
cout<<"常成员函数"<<endl;
}
常成员函数无法更新对象的数据成员,也不能调用没有使用const修饰的成员函数。
3.常数据成员
使用const修饰的数据成员成为常数据成员。
常数据成员必须通过构造函数的成员初始化列表进行初始化,并且不能被更新。
格式如下:
类名::类名(参数列表):常数据成员(值1),常数据成员(值1),....{
}
下面写了一个初始化常数据成员的例子。
class Time {
public:
int const hour;
int const minute;
int const second;
Time(int hour,int minute,int second):hour(hour),minute(minute),second(second){
hour = 0;
minute = 0;
second = 0;
}
};
八、友元
我们可以通过友元的方式在类的外部去访问类的私有成员,包括私有数据成员和私有函数。友元声明使用关键字friend开头。
1.友元函数
在类体中用friend声明,并独立于当前类的外部函数,此函数成为本类的友元函数。
友元函数定义的格式如下:
friend 数据类型 友元函数名(参数列表);
我们以Time类为例,在下面的代码中,我们可以通过友元函数display访问Time类中的私有成员数据。
#include <iostream>
using namespace std;
class Time {
private:
int hour;
int minute;
int second;
public:
Time(int,int,int);
friend void display(Time &time);
};
Time::Time(int hour,int minute,int second){
this->hour = hour;
this->minute = minute;
this->second = second;
}
void display(Time & time){
cout<<time.hour<<":"<<time.hour<<":"<<time.minute<<endl;
}
int main(int argc, const char * argv[]) {
Time time(10,10,10);
display(time);
return 0;
}
2.友元类
我们不仅可以将一个函数声明称友元函数,还可以将一个类声明为另外一个类的友元类。如果类B是类A的友元类。友元类B中的所有函数都是类A的友元函数,可以访问类A中的所有成员。
例如在下面的例子中,我们把ClassB声明为ClassA的友元类。那么ClassB可以访问ClassA中的私有数据成员privateMemberA.
// 声明类B为类A的友元类
class ClassA {
private:
int privateMemberA;
public:
ClassA() : privateMemberA(42) {}
friend class ClassB; // 声明类B为友元类
void displayPrivateMemberA() {
cout << "privateMemberA 的值为: " << privateMemberA << endl;
}
};
// 定义类B
class ClassB {
public:
void accessPrivateMemberA(ClassA &objA) {
cout << "通过类B访问私有成员 privateMemberA: " << objA.privateMemberA << endl;
}
};
九、对象数组
由对象元素组成的数组称为对象数组。
1.定义
对象数组的定义的格式为:
类名 数组名[数组个数];
例如下面的例子中,我们定义两个一个对象数组array,它是Time类型的对象数组。
#include <iostream>
using namespace std;
class Time {
public:
int hours;
int minutes;
int seconds;
};
int main(int argc, const char * argv[]) {
Time array[10];
return 0;
}
2.访问
对象数组和普通的数组访问形式相同,都是通过数组名加上下标访问的,格式如下:
数组名[下标].成员名;
我们可以通过下面的一个例子了解下对象数组的用法:
#include <iostream>
using namespace std;
// 定义 Time 类
class Time {
private:
int hours;
int minutes;
int seconds;
public:
Time() : hours(0), minutes(0), seconds(0) {} // 默认构造函数
Time(int h, int m, int s) : hours(h), minutes(m), seconds(s) {} // 带参数的构造函数
void display() {
cout << hours << ":" << minutes << ":" << seconds << endl;
}
};
int main(int argc, const char * argv[]) {
const int arraySize = 3;
Time timeArray[arraySize] = {Time(8, 30, 45), Time(12, 15, 0), Time(20, 45, 12)};
// 访问并显示数组中的每个时间对象
for (int i = 0; i < arraySize; ++i) {
timeArray[i].display();
}
return 0;
}
十、成员对象
当一个类的成员是另外一个类的对象时,该对象称为成员对象。
说明成员对象的一般形式为:
class 类名{
类名1 成员对象1
类名2 成员对象2
...
类名n 成员对象n
}
成员对象的构造函数调用顺序取决于这些成员对象在类中说明的顺序,与它们在成员初始化列表中给出的顺序无关。析构函数的调用顺序与构造函数正好相反。
下面写了一个例子展示了成员对象的用法:
#include <iostream>
using namespace std;
// 定义一个简单的 Address 类
class Address {
private:
string street;
string city;
string state;
string zipcode;
public:
Address() : street(""), city(""), state(""), zipcode("") {}
Address(string s, string c, string st, string z) : street(s), city(c), state(st), zipcode(z) {}
void display() {
cout << street << ", " << city << ", " << state << " " << zipcode << endl;
}
};
// 定义一个 Person 类,它包含一个 Address 类型的成员对象
class Person {
private:
string name;
int age;
Address address; // Address 类型的成员对象
public:
Person() : name(""), age(0) {}
Person(string n, int a, Address addr) : name(n), age(a), address(addr) {}
void display() {
cout << "Name: " << name << ", Age: " << age << ", Address: ";
address.display();
}
};
int main(int argc, const char * argv[]) {
// 创建 Address 对象
Address addr("123 Main St", "Anytown", "CA", "12345");
// 创建 Person 对象,传递 Address 对象作为参数
Person person("John Doe", 30, addr);
// 显示 Person 对象的信息,包括 Address 对象
person.display();
return 0;
}