C++类和对象2
一 this指针的使用
this指针,在成员函数中,一般指向调用该函数的对象的地址。
平时在直接访问类的数据成员的时候,一般是隐式地使用this指针来访问类的对象的。比如name=s,一般就是this->name=s。
可以使用*this来标识调用该成员函数的当前对象。
静态成员中不能访问this指针,因为静态成员函数不从 属于任何对象。this指针一般用于返回当前对象自身。
https://blog.csdn.net/u011197534/article/details/78385550
可以看这里说明const对象和const成员函数
const对象:在对象前面加上关键字const。该对象的任 何数据成员都不能被修改
如:const Date MyBirthday(8,13,1970);
const成员函数:const写在成员函数的参数表后面,该成 员函数为只读函数。
注意只读成员函数不仅在类定义体 内说明时需要使用关键字const,在类定义体外定义时 也必须使用关键字const。
如:int GetMonth() const{return month;}
volatile对象类似
一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要 加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明, 因为每次对它的读写都可能由不同意义;
有必要使用this指针的意义
1.区分形参或其他地方,同名的情况下,是成员变量还是不是成员变量
2.使用this指针返回当前对象的引用
例如,类Sample成员函数定义如下 Sample& Sample::Set( int I, char *p ) { x=i; ptr=p; return *this; }
一般来说
A &get() //返回对象本身 { return *this; } 改为 A get() //返回对象的一个副本,返回 是对象的一个拷贝,在空间中另一块地方存着和a相同数据的对象。 { return *this; }
二 对象数组和对象指针
建立数组对象时,分别调用构造函数,对每个元素初始化
带参数的构造函数建立数组对象初始化
struct student { char name[10]; int sore1,sore2,sore3; student(char a[10],int d,int b,int c) { strcpy(name,a); sore1=d; sore2=b; sore3=c; } }; int main() { student stud[5]= { student("chenlan",85,78,76), student("huanghong",97,68,87), student("zhanghua",67,98,67), student("yexuan",98,87,76), student("baiwei",87,68,98) }; for (int i=0;i<5;i++) { printf("%s\t%d\t%d\t%d\t\n",stud[i].name,stud[i].sore1,stud[i].sore2,stud[i].sore3); } return 0; }
另外我们显然可以想到,如果没有显式声明无参的构造函数的话
student stud[5]这样的写法显然是编译不通过的。
堆对象
使用new建立的对象属于堆对象,所占内存空间被分配在堆区。
1.利用new建立对象会自动调用构造函数,利用 delete删除对象会自动调用析构函数。
2.如果不主动用delete释放堆对象,那么堆对象的 生存期是这个程序的生命期,只有当程序运行 结束时,堆对象才被删除。
记住new的这三个特性即可把握好new的用法
1.获得一块内存空间
2.调用构造函数
3.返回正确的指针
三 使用对象作为函数的参数
对象做函数的形参,参数之间是单向值传递方式。函数 内部对形参对象的任何修改都不影响实际参数本身。
必须为参数的类创建拷贝构造函数:函数调用之初,需 用实参对象初始化形参对象。
这也是为什么参数传递过程中调用了拷贝构造函数。
下面这个例子可以说明这个传递过程是单向的值传递过程:
#include "student.h" #include <iostream> using namespace std; class Myclass { private: int i; public: Myclass(int n) { i = n; } void Set(int n) { i = n; } int Get() { return i; } }; void Sqr(Myclass obj) { obj.Set(obj.Get() * obj.Get()); cout << "copy of obj has i value of "; cout << obj.Get() << endl; } int main() { Myclass obj(10); Sqr(obj); cout << "But, obj.i is unchanged in main:"; cout << obj.Get(); return 0; }
主函数中输出的仍然是10
如果使用对象的引用作为函数的参数的话
对象引用做函数形参:引用形参成为实参对象的别名, 不产生新对象,无需另外分配内存空间,也不会调用拷 贝构造函数,效率会提高。
此时,参数之间是双向通信方式。
形参指针不能指向新对象
使用对象指针作为函数参数
对象指针做函数形参:传递地址而不是对象本身,效率 会提高。
没有 创建新对象,没有副本的拷贝问题。
对象指针做函数的形参:实参必须是某个对象的地址。
此时,参数之间仍然是单向值传递方式。
形参指针可以指向新对象
四 静态成员
包括静态数据成员和静态成员函数
注意,静态数据成员一定要赋予初值。
#include "student.h" #include <iostream> using namespace std; class Ctype { private: int a; static int s; //定义私有的静态数据成员s public: void Print(); Ctype(int x= 0); }; void Ctype::Print() { cout << "a=" << ++a << endl; cout << "s=" << ++s << endl; } Ctype::Ctype(int x) { a = x; } int Ctype::s = 0; int main() { Ctype c1, c2, c3; c1.Print(); c2.Print(); c3.Print(); return 0; }
静态成员函数
静态成员函数只能直接访问属于该类的静态数据,但 不能直接调用类中的非静态成员。
可以不定义类的具体对象
而类名::静态函数名()而直接使用静态函数。
注意事项:
1.静态成员函数可以定义为内嵌的,也可以在类外定义, 在类外定义时,不用static
class Student { public: static void PrintfAllStudents(); }; void Student::PrintfAllStudents() { } void main() { }
像这样
2.静态成员函数没有this 指针因此在静态成员函数中隐 式或显式地引用这个指针都将导致编译时刻错误
3.静态成员函数的目的通常是为了访问全局变量或静态 数据成员
4.一般情况下,静态成员函数不能访问类的非静态成员。 但如果确实需要,静态成员函数可通过对象名(或指 向对象的指针)来访问该对象的非静态成员
如 cout<<a.width<<endl;
代码如下
// 示例程序,静态成员函数通过对象访问非静态成员。 #include <iostream> using namespace std; class Tc { public: Tc(int c){a=c;b+=c;} //静态成员函数,通过对象C调用非静态成员 static void display(Tc C) { //错误,静态成员函数不能直接访问非静态成员 cout<<“a=”<<a<<“,b=”<<b<<endl; cout<<“a=”<<C.a<<“,b=”<<b<<endl; //正确,通过对象访问 } private: int a; static int b; //静态数据成员 }; int Tc::b=2; //静态数据成员的初始化 int main() { Tc A(2),B(4); Tc::display(A); //对象A作为调用函数的参数 Tc::display(B); //对象B作为调用函数的参数 return 0; }
5.编译程序把静态成员函数限定为内部连接
插入一个完整的使用例子
#include <iostream> #include<string> using namespace std; class Student { public: Student(string pname = "no name"); ~Student(); static int number() //静态成员函数 { return total; //返回静态数据成员的值 } protected: //保护数据成员 static int total; //静态数据成员,代表学生人数 string name; }; int Student::total = 0; Student::Student(string pname) { cout << "create one student " << endl; name = pname; total++; //静态成员:每创建一个对象,学生人数增1 cout << total << endl; } Student::~Student() { cout << "destruct one student" << endl; total--; //静态成员:每析构一个对象,学生人数减1 cout << total << endl; } void fn() //普通函数 { Student sl; Student s2; //调用静态成员函数用类名引导 cout << Student::number() << endl; } int main() { fn(); //此时对象已不存在但静态成员仍存在 cout << Student::number() << endl; return 0; }
结果为
create one student 1 create one student 2 2 destruct one student 1 destruct one student 0 0
其他注意点
1. 理解了带默认参数的构造函数如果去掉默认参数,和重载构造函数一致的话
为什么会报错
一开始这里写了
Student(string pname = "no name");
以及student()
两个构造函数,但是这样显然是不对的,上面的是能够包含下面的,所以编译器会报错。
同时,第一个构造函数能够满足student s1,s2这样的无参构造对象。
2.静态成员函数的调用方法
五 友元
友元函数是在类声明中由关键字friend修饰说明的非成 员函数,在它的函数体中能够通过对象名访问 private 和 protected成员
注意引用的使用,把这个类作为参数。
友元函数示例
// 示例程序,友元函数。计算屏幕上两点之间的距离。 #include <iostream> #include <cmath> using namespace std; class TPoint { public: TPoint(double a,double b) { x=a; y=b; cout<<"point:("<<x<<","<<y<<")"<<endl; } friend double distance(TPoint &a,TPoint &b); //友元函数的声明 private: double x,y; }; double distance(TPoint &a,TPoint &b) //被定义为友元的普通函数 { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); //访问私有成员 } int main() { TPoint p1(2,3),p2(4,5); cout<<"the distance between two point is:"<<distance(p1,p2)<<endl; return 0; }
注意上面实现的时候,写的是
double distance(TPoint &a,TPoint &b)
而不是
double TPoint::distance(TPoint &a,TPoint &b),说明这只是个普通函数
而不是类的成员函数。
友元成员
使类B中的成员函数成为类A的友元函数,这样类B的该成员函数就可以访问类A的所有成员了。
就是在B类中定义一个A类的成员函数为B的友元函数
这样A类的这个函数就可以任意访问B的成员了
举例:
#include <iostream> using namespace std; class N; class M { public: M(int x,int y) { a=x; b=y; } void Print() { cout<<"a="<<a<<"b="<<b<<endl; } void Setab( N &); private: int a,b; }; class N { public: N(int a,int b) { c=a; d=b; } void Print() { cout<<"c="<<c<<"d="<<d<<endl; } fiend void M::Setab( N &); private: int c,d; }; void M::Setab(N &obj) { a=obj.c; b=obj.d; }
友元类
声明示例如下
#include <iostream> using namespace std; #include <math.h> class A { public: A(){x=5;} friend class B; //友元类的声明,B是A的友元类 private: int x; };
注意类之间的友元关系不具有传递性以及交换性。
六 对象成员
A类的对象作为B类的成员属性出现,则我们称其为B类的一个对象成员。对象成员其中包含两层意思,首先它是某个类的对象,其次它是另一个类的成员。
对象成员函数的构造函数先于 本类构造函数执行,执行顺序 与定义对象成员的顺序相同
析构函数的执行顺序与构造函 数的相反