C++之类和对象的使用(三)

对象数组

如果构造函数只有一个参数,在定义数组时可以直接在等号后面的花括号内提供。Student stud[3]={90,92,01};//合法

如果构造函数有多个参数,则不能用在定义时直接所提供所有实参的方法。

但可以如下定义:

//构造函数有三个参数:学号,年龄,成绩
Student stud[3]{

Student(1000,19,22);
Student(1001,19,22);
Student(1002,19,22);
};

  对象指针

指向对象的指针

class Box
{public:
    Box(int h=10,int w=10,int len=10);
    int s;
    int volume();
 private:
     int height;
     int width;
     int length;
};
Box::Box(int h,int w,int len)
{
    height=h;
    width=w;
    length=len;
}

int Box::volume(){
    return(height*width*length);
}
Box *ptr;//定义指向对象的指针
Box t1;//定义Time类对象
ptr=&t1;//将t1的起始地址赋给ptr

指向对象成员的指针

1)指向对象数据成员的指针

定义的一般形式为:

数据类型名 *指针变量名;

int *p1;
p1=&t1.s;// s是public类型数据,则可以在类外通过指向对象数据成员的指针变量访问对象数据成员s
cout<<*p1<<endl;//输出t1.hour的值

2)指向对象成员函数的指针

定义指向对象成员函数的指针变量和定义指向普通函数的指针变量方法有所不同。对比如下:

类型名(*指针变量名)(参数列表);
void (*p)( );
p=fun;
(*p)( );//调用普通fun函数
//////////////////////////////////////////
针对指向对象成员函数的指针要求:指针变量的类型必须与赋值号右侧函数的类型匹配,满足一下3个方面
1)函数参数的类型和参数个数
2)函数返回值的类型
3)所属的类
采用如下格式:

一般形式为:
数据类型名(类名::*指针变量名)(参数列表);
#include<iostream>
using namespace std;
class Time
{public:
    Time(int,int,int);
    int hour;
    int minute;
    int sec;
    void get_time(); 
};
Time::Time(int h,int m,int s)
{
    hour=h;
    minute=m;
    sec=s;
}
void Time::get_time(){
    cout<<hour<<":"<<minute<<":"<<sec<<endl; 
}
int main()
{
    Time t1(10,2,39);
    int *p1=&t1.hour;
    cout<<*p1<<endl;
    t1.get_time();
    Time *p2=&t1;
    p2->get_time();
    void(Time::*p3)();//定义指向Time类公用成员函数的指针变量p3
    p3=&Time::get_time;//使p3指向Time类公用成员函数get_time
    (t1.*p3)();//调用p3指向的成员函数(t1.get_time);不应写成p3=&ti.get_time(成员函数不是存放在对象的空间中的,而是存放在对象外的空间中的。)
}        // main函数第8.9两行可以写成一行void(Time::*p3)()=&Time::get_time

this指针

 在每一个成员函数中都包含一个特殊的指针,这个指针的名字是固定的,称为this指针。

它是指向本类对象的指针,它的值是当前被调用的成员函数所在的对象的起始地址。

//当调用a.volume时候,实际 上执行
(a.height)*(a.width)*(a.length)

  公用数据的保护

既要使数据能在一定范围内共享,又保证它不被任意修改,这是可以把有关的数据定义为常量。

常对象

可以在定义对象的时候加关键字const,指定对象为常对象。常对象必须要有初值,如

Time const t1(23,67,7);

这样对象t1中的所有数据成员的值都不能被修改。

类名 const 对象名(实参表)或者  const 类名 对象名(实参表)

Tips:

1)如果一个对象被声明为常对象,则通过该对象只能调用它的常成员函数,而不能调用该对象的普通成员函数(除了析构函数和构造函数)。常成员函数时常对象唯一的对外接口。

引用常对象中的数据成员,只须将该成员函数声明为const即可。 void get_time() const;

2)常成员函数可以访问常对象中的数据成员,但仍然不能修改其值。若对数据成员声明为mutable,可以被修改。

以上两点保证常对象中数据成员的值绝对不会改变。

常对象成员(数据成员和函数成员)

常数据成员

其作用和用法与一般常变量相似,用关键字const来声明常数据成员。其值不可修改;

只能通过构造函数的参数初始化表对常数据成员进行初始化,任何其他函数都不能对常数据成员赋值。

常成员函数

如果将成员函数声明为常成员函数,则只能引用本类中的数据成员,而不能修改它们。

声明常成员函数的一般形式:

类型名 函数名 (参数表)const

const是函数类型的一部分,在声明函数和定义函数时都要有const关键字,在调用时不必加const。常成员函数可以引用const数据成员,也可以引用非const的数据成员。

不用误以为常对象中的成员函数都是常成员函数,常对象只保证其数据成员是常数据成员,其值不被修改。如果在常对象中的成员函数未加const声明,系统把它编译为非const成员函数。

常成员函数不能调用另一个非const成员函数。

指向对象的常指针

将指针变量声明为const类型,这样指针变量始终保持为初值,不能改变,即其指向不变。

Time t1(2,2,3),t2;
Time *const ptr1;
ptr1=&t1;
ptr1=&t2;//错误,不能改变ptr1的指向

类名 *const 指针变量名

指向常对象的指针变量

常变量指针

const char *ptr;

定义指向常变量的指针变量的一般形式为:

const 类型名 *指针变量名;

1)如果一个变量已被声明为常变量,只能用指向常变量的指针指向它。

不能用一般的(指向非const型变量的)指针去指向它;如

const char c[ ]="boy";
const char *p1;
p1=c;
char *p2=c;//不合法,p2不是指向常变量的指针变量

2)指向常变量的指针变量除了可以指向常变量外,还可以指向未被声明为const的变量。此时不能通过此指针变量改变该变量的值。

char c1='a';
const char *p;
p=&c1;
*p='b';//非法,不能通过p改变变量c1的值
c1='b';

3)如果函数的形参是指向非const型变量的指针,实参只能用指向非const变量的指针,而不能用指向const变量的指针。这样,在执行函数的过程中可以改变形参指针变量所指向的变量的值。如果函数的形参是指向const型变量的指针,在执行函数的过程中显然不能改变指针变量所指向的变量的值,因此允许实参是指向const变量的指针,或指向非const变量的指针。如

const char str[]="boy";
void fun(char *ptr);
fun(str);//调用fun函数,实参是const变量的地址,非法

用指针变量作形参时形参和实参的对应关系

对象的常引用

 类似于变量的引用

 const型数据的总结

  对象的动态建立与释放

 前面介绍的方法定义的对象都是静态的,在程序运行过程中,对象所占的空间是不能随时释放的。

动态建立对象:要用到对象的时候建立对象,不用的时候就撤销它,释放它所占的内存空间。

如new Box;

编译系统开辟了一段内存空间,并在此空间中存放一个Box类对象,同时调用该类的构造函数,以使该对象初始化。但是此时用户无法访问这个对象,因为这个对象既没有对象名,用户不知道它的地址。这种对象成为无名对象,存在但没有名字。

用new运算符动态地分配内存后,将返回一个指向新对象的指针的值,即所分配的内存空间的起始地址。用户可以获得这个地址,并通过这个地址来访问这个对象。需要定义一个指向本类的对象的指针变量来存放该地址。

Box *pt;
pt=new Box;

C++还允许在指向new时,对新建立的对象进行初始化。

Box *pt = new Box(12,13,18);

这种写法把上面两个语句合并为一个语句,并指定初值。

在不需要使用由new建立的对象时,可以用delete运算符予以释放。

delete pt;

在执行delete运算符时,在释放内存空间之前,自动调用析构函数,完成有关善后清理工作。

  对象的赋值和赋值

 对象的赋值

对象名1 = 对象名2

Student stud1,stud2;
....
....
stud2=stud1;

1)对象的赋值只对其中的数据成员赋值,而不对成员函数赋值。不同对象的成员函数 是同一个函数代码段,不需要,也无法对它们赋值。

2)类的数据成员中不能包括动态分配的数据,否则在赋值时可能出现严重后果。

对象的复制

有时需要用到多个完全相同的对象,即对象的复制。类名 对象2(对象1)。如:

Box box2(box1);//用已有的对象box1去克隆一个新对象box2

C++还提供另一种方便用户的复制形式,用复制号代替括号。

Box box2=box1;

类名 对象名1=对象名2;

区别对象的复制与赋值:

对象的赋值是对一个已经存在的对象赋值,因此必须先定义被赋值的对象。而对象的复制是从无到有地建立一个新对象,并使它与一个已有的对象完全相同。

静态数据成员:如果想在同类的多个对象之间实现数据共享,也不用使用全局对象,可以用静态的数据成员。

class Box{

public:
   int volume();
private:
   static int height;// 静态的数据成员
   int width;
   int length;
};

如果希望各对象中的数据成员的值是一样的,就可以把它定义为静态数据成员;

静态数据成员只占用一份空间(而不是每个对象分别为它保留一份空间)。

说明:

1)如果只声明了类而未定义对象,则类的一般数据成员是不占内存空间的,只有在定义对象时,才为对象的数据成员分配空间。但是静态数据成员不属于某一个对象,在为对象所分配的空间中不包括静态数据成员所占用的空间。静态数据成员是在所有对象之外单独开辟空间。只要类中指定了静态数据成员,即使不定义对象,也为静态数据成员分配空间,它可以被引用。

2)静态数据成员是在程序开始运行时被分配空间,到程序结束时才释放空间。

3)静态数据成员可以初始化,但只能在类外进行初始化。

int Box::height = 10;

一般形式为:

数据类型  类名::静态数据成员名=初值;

在初始化时不必加static。不能用参数初始化表对静态数据成员初始化。默认值为0。下面是错误的:

Box(int h,int w,int len):height(h){}//错误

4)静态数据成员既可以通过对象名引用,也可以通过类名来引用。

#include<iostream>
using namespace std;

class Box
{public:
//    Box();
    Box(int,int);
    int volume();
 //private:
     static int height;
     int width;
     int length;
};
Box::Box(int w,int len)
{
    width=w;
    length=len;
}

int Box::volume(){
    return(height*width*length);
}
 int Box::height=10;//对静态数据成员初始化 

int mian()
{
    Box box1(18,29),box2(18,2);
    //cout<<"the box1 is:"<<box1.volume()<<endl;
    cout<<box1.height<<endl;//通过对象名引用静态数据成员 
    cout<<box2.height<<endl;
//    Box box2(14,12,48);
//cout<<"the box2 is:"<<box2.volume()<<endl;
    cout<<Box::height<<endl;//通过类名引用静态数据成员 
    cout<<box1.volume()<<endl;
   // return 0;
 } 

可以看到,如果静态数据成员是public型,在类外可以通过对象名引用公用的静态数据成员,也可以通过类名引用静态数据成员。即使没有定义对象,也可以通过类名引用静态数据成员,说明静态数据成员属于类,不属于对象。若是private,则不可在类外直接引用,必须通过公用的成员函数引用。

5)有了静态数据成员,各对象之间的数据有了沟通的渠道,实现数据共享。因此可以不使用全局变量。注意公用静态数据成员与全局变量不同,静态数据成员的作用域只限于定义该类的作用域内。

 静态成员函数

成员函数也可以是静态的,在类中声明函数的前面加static即可。

static int volume();

静态成员函数时类的一部分而不是对象的一部分。如果要在类外调用公用的静态成员函数,要用类名和域运算符“::”。

Box::volume();

实际上也越小通过对象名调用静态成员函数。如 a.volume( );但这不意味着此函数属于对象a,而只是用a的类型而已。

静态成员函数是为了处理静态数据成员。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小樱的经验随笔

关于int *a[常量]与int (*a)[常量]的分析与区分(详解)

前言: 小伙伴私信我说,int *a[常量]与int (*a)[常量]这个区分不开,C指针,确实是C中最难的部分,也是学C++,JAVA,包括你以后上岗用的非常...

2723
来自专栏猿人谷

static_cast ,reinterpret_cast

用法:static_cast < type-id > ( expression ) 该运算符把expression转换为type-id类型,但没有运行时类型检查...

22910
来自专栏无所事事者爱嘲笑

数组方法整理

1414
来自专栏PHP在线

PHP函数

请点击上面蓝色PHP关注 你知道这些简单的函数中的方法吗? count() 函数计算数组中的单元数目或对象中的属性个数。 对于数组,返回其元素的个数,对于其他值...

2975
来自专栏机器学习原理

正则表达式符号方法

前言: re模块是爬虫的基础,文章主要从符号和用法来介绍它的基础用法 符号 各个符号及用法如下: 符号 含义 . 表示匹配除了换行符外的任...

3206
来自专栏haifeiWu与他朋友们的专栏

Python基础(一)

以#开头的语句是注释,解释器会忽略掉注释。其他每一行都是一个语句,当语句以冒号:结尾时,缩进的语句视为代码块。

1585
来自专栏软件开发 -- 分享 互助 成长

C++ 隐式类型转换

C++定义了一组内置类型对象之间的转换标准,在必要的时候它们被编译器隐式的转换 1、任何两种或多种类型的数据和变量混合操作的时候,最宽的数据类型成为目标转换类型...

2327
来自专栏深度学习思考者

Python学习(二) 正则表达式

Python正则表达式 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。re 模块使 Python 语言拥有全部的正则表达式功...

2059
来自专栏Jerry的SAP技术分享

聊聊JavaScript和Scala的表达式 Expression

函数f的实现,会检查这两个参数的类型,如果是函数,则执行函数调用,再打印其返回值,否则直接打印传入的表达式的值。

872
来自专栏天天

数据类型的转换

1193

扫码关注云+社区

领取腾讯云代金券