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类的一个对象成员。对象成员其中包含两层意思,首先它是某个类的对象,其次它是另一个类的成员。

 

 对象成员函数的构造函数先于 本类构造函数执行,执行顺序 与定义对象成员的顺序相同

   析构函数的执行顺序与构造函 数的相反

 

七 前向引用声明 和范例

posted @ 2019-11-19 22:32  TheDa  阅读(169)  评论(0编辑  收藏  举报