c++面向对象程序设计教程
this指针
同一类的各个对象创建后,都在类中产生自己成员的副本。而为了节省存储空间,每个类的成员函数只有一个副本,成员函数由各个对象调用。
那么对象在副本中如何与成员函数建立关系?c++为成员函数提供了一个称为this的指针,当创建一个对象的时候,this会自动初始化指向该对象,当某一个对象调用一个成员函数的时候,this指针将作为一个变量传递给该函数。所以,不同的对象调用一个成员函数的时候,编译器会根据this指针来确定应该引用哪一个对象的数据成员。
下面用代码来初步认识一下this指针:
#include<iostream>
using namespace std;
class Point
{
public:
Point(int =0,int =0);
void displayxy();
private:
int X, Y;
};
Point::Point(int x,int y)//双参构造函数;
{
X = x;
Y = y;
}
void Point::displayxy()
{
cout << X << endl;
//cout<<this->X<<endl;
cout << Y << endl;
//cout<<this->Y<<endl;
}
//在这里我们可以看到,this指针是一种隐函数对象指针,它指向的是该函数的数据成员;
int main(void)
{
Point obj1(10, 20), obj2(8, 9), * p;
p = &obj1;//p指向对象obj1;
p->displayxy();
p = &obj2;//p指向对象obj2;
p->displayxy();
}
通过上述代码,我们可以发现,this指针并不会被表示出来,也就说明它是一个隐含对象指针。
那么this指针有哪些属性喃?
this指针的属性 | |
---|---|
this是一个局部量,局部于某个对象 | this是一个常量,它不能作为赋值,递增,递减等运算的目标对象 |
只有非静态类成员函数才拥有this指针并通过该指针来处理对象 | 实际中,通常不去显示使用thi是指针引用数据成员和成员函数 |
子对象和堆对象
1.子对象的声明*
一个对象作为另一个类的成员时,该对象称为该类的子对象。子对象实际上也是某个类的成员。
*
//c++面向对象——类
#include<bits/stdc++.h>
using namespace std;
class MyClass
{
public:
MyClass(int =0,int =0);//无参构造函数;
~MyClass();//析构函数;
void destroyxy();
private:
int X, Y;
};
//构造函数;
MyClass::MyClass(int x,int y)
{
X = x;
Y = y;
cout << "Destroy is called!" << endl;
}
//构造析构函数;
MyClass::~MyClass()
{
cout << "Destroy is called!!!" << endl;
}
void MyClass::destroyxy()
{
cout << "Destroy is called!";
cout << setw(4) << "(" << this->X << "," << this->Y << ")" << endl;
}
class Whole
{
public:
Whole(int);
Whole(int, int);
~Whole();
private:
MyClass p1, p2;//子对象;
};
Whole::Whole(int x)
{
cout << "Construct of Whole" << endl;
cout << x << endl;
}
Whole::Whole(int x, int y) :p1(x), p2(y)
{
cout << "Construct is called" << endl;
}
Whole::~Whole()
{
cout << "end of this" << endl;
}
int main(void)
{
Whole whole(3, 4);
Whole w(3);
}
通过上面的代码我们可以很明确的看出有一行代码的格式:Whole::Whole(int x, int y) :p1(x), p2(y)
那么这行代码又代表什么意思喃?
2.子对象的初始化
为初始化子对象,X的构造函数要调用这些对象成员所在类的构造函数,于是X类的构造函数中就应该包含数据成员初始化列表,用来给于对象进行初始化;
<类>::<类>(<参数>):<成员1>(参数),<成员2>(参数)~~
{
~~
}
注意:冒号后由逗号隔开的想组成数据成员初始化列表,成员初始化列表是由一个或者多个项组成,多个选项之间由逗号隔开。可以是对子对象进行初始化,也可以是对该类中其他的数据成员初始化。对子对象的构造函数的调用函数的舒徐取决于这些子对象在类中的说明顺序,与数据成员初始化列表顺序无关。
堆对象
堆对象是指在程序运行时根据需要随时可以被创建和删除的对象,在虚拟的程序空间中存在一些空闲存储单元,这些存储单元组成我们所谓的堆。
c++程序的内存格局通常分为4个区:
创建和删除堆对象可以用如下两个运算符,new,delete;
l下面我们用一段代码来认识这两种运算符的用法;
#include<iostream>
using namespace std;
class Heapclass
{
public:
Heapclass();//构造函数;
~Heapclass();//析构函数;
private:
int i;
};
Heapclass::Heapclass()
{
cout << "Default Constructor is called." << endl;
}
Heapclass::~Heapclass()
{
cout << "Destructor is called" << endl;
}
int main(void)
{
Heapclass* ptr;
ptr = new Heapclass[2];//申请动态空间表明有两个元素,每个元素都是该类的对象;
if (ptr)
{
cout << "Out of Memory!" << endl;
}
cout << "Exit main" << endl;
delete[]ptr;
return 0;
}
通过程序运行结果我们可以发现,最后释放对象的时候,调用了两次析构函数,创造对象的时候,一共调用了2次构造函数,所以,通过这里,我们也不难发现构造函数和析构函数的用途;
类的静态成员
上面我们创建了对象后,对其进行了空间申请,但是,每次用完后,都会释放,那如果我们要共享某一个对象的数据成员的时候,我们又应该怎么做喃?那就要用到我们下面要学到的知识,静态成员;
1.静态数据成员
//静态数据成员不属于任何对象,它不因对象的建立而产生,也不因对象的析构而删除,它是类定义的一部分,所以使用静态
//数据成员不会破坏类的隐蔽性,类中的静态数据成员不同于一般的静态变量,也不同于其他数类数据成员,它在程序开始运行时创建而不是在对象创建时创建。
//他所占空间的回收也不是在析构函数时进行而是在程序结束时进行;
2.静态数据成成员初始化
必须对静态数据成员初始化,因为这样程序在编译的时候才会为静态数据成员分配一个具体的数据存储空间。
静态数据成员的初始化u一般的数据成员不同,他的初始化不能在构造函数中完成,静态数据成员格式化为:
<数据类型><类名>::<静态数据成员>=<初始值>;
这里的作用域运算符“::”用来说明数据成员所属类;
静态数据成员的引用
静态数据成员可以说明为共有的,私有的或保护的,若为公有的可直接访问,引用静态数据成员的格式:
<类名>::<静态数据成员>;;
由于静态数据成员是类的成员,用对象名也可以。但通常用上述方法引用,因为静态数据成员不从属于任何一个具体对象;
//静态数据成员不属于任何对象,它不因对象的建立而产生,也不因对象的析构而删除,它是类定义的一部分,所以使用静态
//数据成员不会破坏类的隐蔽性,类中的静态数据成员不同于一般的静态变量,也不同于其他数类数据成员,它在程序开始运行时创建而不是在对象创建时创建。
//他所占空间的回收也不是在析构函数时进行而是在程序结束时进行;
#include<iostream>
using namespace std;
//定义点类;
class Point
{
public:
static int countP;//静态数据成员必须初始化,他不能在构造函数中初始化,静态数据成员的初始化格式为:
//<数据类型><类名>::<静态数据成员>=<初始值>;
Point(int = 0, int = 0);
~Point();//析构函数;
private:
int X, Y;
};
Point::Point(int x, int y)
{
X = x;
Y = y;
cout << " Constructor is called!" << endl;
countP++;//每创建一个对象,数值加一;
}
Point::~Point()
{
cout << "Destructor is called!" << endl;
countP--;//每析构一个对象,点数-1;
cout << "现在对象数是:" << countP << endl;
}
//静态数据成员初始化和定义;
int Point::countP = 0;
//主函数;
//静态数据成员的引用的格式为:
//<类名>::<静态数据成员>;
int main(void)
{
Point p(4, 5);//第一个对象;
cout << "现在对象数是:" << Point::countP << endl;
Point B(7, 8);//第二个对象;
cout << "现在对象数是:" << B.countP << endl;
}
静态成员函数
静态成员函数的定义和其他成员函数一样,静态成员函数与静态数据成员类似,从属于类,在一般函数前面加上static关键字;
调用静态成员函数的格式为:
<类名>::<静态成员函数>(参数);
或者
<对象名>.<静态成员函数>(参数);
#include<iostream>
using namespace std;
//定义点类;
class Point
{
public:
Point(int = 0, int = 0);
~Point();
static void dispcount();
private:
int X, Y;
static int countP;
};
Point::Point(int x, int y)
{
X = x;
Y = y;
cout << "Constructor is called!" << endl;
countP++;
}
Point::~Point()
{
cout << "Destructor is called!" << endl;
countP--;
cout << "现在的对象数是:" << Point::countP << endl;
}
void Point::dispcount()
{
cout << "现在的对象数:" << Point::countP<<endl;
}
int Point::countP = 0;//静态数据成员初始化;
int main(void)
{
Point A(4, 5);
Point::dispcount();
Point B(7, 8);
Point::dispcount();
return 0;
}
通过上述代码我们可以发现对于静态成员函数的引用就只有两种方式,由于没有this指针,故就通过对象来访问。
类的友元
上面的程序我们不难发现,我们在构造函数和成员的时候,都是在公有成员和私有成员中操作的,那么保护成员和私有成员的数据又该如何访问?
那就要用到接下来学到的友元函类;
友元函数
在类里声明一个普通函数,加上关键字friend,就成了该类的友元函数,他可以访问类中一切成员。
friend<函数类型><友元函数名>(参数列表);
例如:用友元函数求两点的距离
#include<iostream>
#include<cmath>
using namespace std;
class Point
{
public:
Point(double xi, double yi) { X = xi, Y = yi; };
void destroy();
friend double length(Point& a, Point& b);
private:
int X, Y;
};
double length(Point& a, Point& b)//不需要将友元函数设计为成员函数;
{
double dx = a.X - b.X;
double dy = a.Y - b.Y;
return sqrt(dx * dx + dy * dy);
}
int main(void)
{
Point p1(3, 5), p2(4, 6);
double d = length(p1, p2);
cout << "The distence is" << d << endl;
}
上面的代码我们可以发现,在对友元函数进行初始化的时候,是不需要表明类的。也就是说,一个普通的函数可以定义成类的友元函数,一个类的成员函数也可以定义成另一个类的友元函数,友元成员函数的使用与一般友元函数的使用基本相同,只是通过相应的类或者对象名来引用。
友元类
除了函数外,一个类也可以被声明为另一个类的友元,该类被称作友元类。
假设有类A,和类B,若在类B的定义中将类A声明为友元,那么,类A被称为类B的友元类,它所有的成员函数都可以访问类B中的任意成员。友元类的声明格式:
friend class<类名>;
例如:将整个教师类teacher看成是学生类student的友元类,教师可以给学生设置学号,输入学生成绩;
#include<iostream>
using namespace std;
class student
{
public:
friend class teacher;
student() {};//构造函数;
private:
int number, score;
};
class teacher
{
public:
teacher(int i, int j);
void displayxy();
private:
student a;
};
teacher::teacher(int i, int j)
{
a.number = i;
a.score = j;
}
void teacher::displayxy()
{
cout << "No=" << a.number<<endl;
cout << "score=" << a.score << endl;
}
int main(void)
{
teacher t1(1001, 89), t2(1002, 78);
cout << "第一个学生的信息:";
t1.displayxy();
cout << "第二个学生的信息:";
t2.displayxy();
}
友元的作用主要是为了提高小路和方便编程,但友元破坏了类的整体性,使用时要权衡利弊;
学习了这么多,我们来来来练习一下吧!
学生成绩管理系统:
#include<iostream>
#include<string>
using namespace std;
class Student
{
int num;//学号;
string name;//姓名
float score;//成绩
Student* per;//当前节点;
Student* next;//下一个结点指针
public:
Student();//构造函数;
Student* find(int i_num);//查找指定学好的学生;
void edit(string i_newname, float i_score);//修改学生信息;
void erase();//删除指定学号的学生;
int add(Student* i_newStudent);//增加学生;
int getno();//获得学生学号;
string getname();//获得学生姓名;
float getscore();//获得学生成绩;
static int maxno;//当前最大学号;
};
//构造函数;
Student::Student()
{
score = 0.0;
per = NULL;
next = NULL;
}
//查找指定学好的学生函数;
Student* Student::find(int i_num)
{
if (i_num == num)
return this;
if (next != NULL)
{
return next->find(i_num);//递归;
}
return NULL;
}
//修改学生的名字的函数;
void Student::edit(string i_name, float i_score)
{
if (i_name == "")
return;
name = i_name;
score = i_score;
}
//删除指定学号的学生函数;
void Student::erase()
{
if (num < 0)
return;
if (per != NULL)
per->next = next;
if (next != NULL)
next->per = per;
next = NULL;
per = NULL;
}
//增加学生函数;
int Student::add(Student* i_newStudent)
{
int num = maxno + 1;
while (true)
{
if (NULL == find(num))
break;
num += 1;
}
Student* tmp = this;
while (true)
{
if (tmp->next == NULL)
break;
tmp = tmp->next;
}
tmp->next = i_newStudent;
i_newStudent->next = NULL;
i_newStudent->per = tmp;
i_newStudent->num = num;
return num;
}
//可以得到以下的相关信息函数;
int Student::getno() { return num; }
string Student::getname() { return name; }
float Student::getscore() { return score; }
//静态成员的初始化及程序的主函数;
int Student::maxno = 1000;
int main()
{
Student* studentroot = new Student();
string input1;
float input2;
Student* tmp = NULL;
while (true)
{
cout << "输入指令:查找(F),增加(A),编辑(E),删除(D),退出(Q)" << endl;
cin >> input1;
if (("F" == input1) || ("f" == input1))
{
cout << "输入学号:";
int id = -1;
cin >> id;
tmp = studentroot->find(id);
if (tmp == NULL)
{
cout << "没找到" << endl;
continue;
}
cout << "学号:" << tmp->getno();
cout << "姓名:";
string name;
if ((name = tmp->getname()) != "")
{
cout << name << endl;
}
else
{
cout << "未输入" << endl;
cout << "成绩:" << tmp->getscore() << endl;
}
}
else if ((input1 == "A") || (input1 == "a"))
{
cout << "输入姓名,成绩:";
cin >> input1 >> input2;
tmp = new Student();
tmp->edit(input1, input2);
cout << "学号:" << studentroot->add(tmp) << endl;
}
else if (input1 == "E" || input1 == "e")
{
cout << "输入学号:";
int id = 0;
cin >> id;
tmp = studentroot->find(id);
if (tmp == NULL)
{
cout << "空号" << endl;
continue;
}
cout << "新姓名,新成绩:";
cin >> input1 >> input2;
tmp->edit(input1, input2);
cout << "更改成功" << endl;
}
else if ((input1 == "D") || (input1 == "d"))
{
cout << "输入学号:";
int id = 0;
cin >> id;
tmp = studentroot->find(id);
tmp->erase();
cout << "已成功删除" << endl;
delete tmp;
}
else if ((input1 == "Q") || (input1 == "q"))
{
break;
}
else
{
cout << "输入有误!" << endl;
}
}
delete studentroot;
return 0;
}
感兴趣的同学可以自己尝试去运行一下,也可以在现有的程序上进行增添.