c++ 吕凤翥 第六章 类和对象(二)
c++ 吕凤翥 第六章 类和对象(二)
指针 引用 和数组
一:对象指针和对象引用
1.指向类的成员的指针
分为指向成员变量和指向成员函数两种指针
成员变量的格式: 类型说明符 类名:: * 指针名
成员函数的格式: 类型说明符 (类名::* 指针名)(参数表)
class A
{
public:
int fun(int b){return ...}
A(int i){a=i;}
int c;
private:
int a;
}
定义指向类A 的数据成员c的指针pc : int A::*PC=&A::c; 此时c为公有成员
定义指向类A的成员函数fun的指针pfun:int (A::*pfun)(int)=A::fun; 函数名为地址 所以未用取地址符号 fun也是公有成员函数 所以可以直接指向
先创建对象,然后通过对象来引用指针指向的成员
A a;
a.* pc=8;
使用指向对象的指针通过指向类成员的指针对该成员进行操作时,可以使用运算符->* 运算符前面为指向对象的指针,后面为指向类成员的指针
A * p=&a; //a 为A类的对象 p是指向对象a的指针
p->*pc=8; //pc为指向A类的成员c的指针
指向普通函数的指针的声明格式:(非类中的成员函数)
类型说明符 * 指针名 (参数表)
赋值格式: 指针名 =函数名 即指针变量的值为函数名即地址
调用格式: (* 指针名)(实参表)
如果是指向类的成员函数的指针 则应加上相应的对象名或指向对象的指针名以及对象成员运算符。
案例:
//吕 example 6.1
#include <iostream>
using namespace std;
class A
{
public:
A(int i){
a=i;
}
int fun(int b)
{
return a*c+b;
}
int c;
private:
int a;
};
int main()
{
A x(8); //构造
int A::*pc;// pc指向类A
pc=&A::c;//pc指针初始化 为公有成员c
x.*pc=3; //成员变量赋值
int(A::*pfun)(int);//指向成员函数的指针声明
pfun=A::fun;//初始化指向成员函数的指针
A*p=&x; // 声明 初始化指向对象的指针
cout<<(p->*pfun)(5)<<"\n";
}
上例中 指针的性质是不同的 p指向对象 pc和pfun是指向类中的成员
2.对象指针和对象引用作为函数参数
使用对象指针做函数参数更加普遍
特点:
1.传地址调用 可改变实参的值(被调函数中改变调用函数中的参数值),实现函数之间的信息传递
2.使用对象指针作为形参仅将对象的地址值传给形参,而不进行对象副本的复制(函数传值的方式 ) 提高运行效率,减少开销
传递的实参必须是对象的地址
案例2:
//吕6.2 example
#include <iostream>
using namespace std;
class M
{
public:
M() //default c
{
x=y=0;
}
M(int _x,int _y) //c
{
x=_x;
y=_y;
}
void copy(M * m); //member #1
void setxy(int _x,int _y) //member #2
{
x=_x;y=_y;
}
void print() //member #3
{
cout<<x<<","<<y<<"\n";
}
private:
int x,y;
};
void M::copy(M * m) //implementation m为指向M对象的指针
{
x=m->x;
y=m->y;
}
void fun(M m1,M * m2); //function prototype 非类中的函数
int main()
{
M p(5,7),q;//两个对象 一个非默认 一个默认
q.copy(&p);
fun(p,&q);//p的值不变 传递的是副本 q的值变化 传递的是对象的地址
p.print(); //5 7
q.print(); //22 25
return 0;
}
void fun(M m1,M * m2)//使用对象指针作为参数 传递
{
m1.setxy(12,15);
m2->setxy(22,25);
}
2.对象引用作为函数参数
使用对象引用做参数比指针更加的普遍 用引用更加方便 更加直接
example 6.3
与6.2的区别
void copy(M &m); //&取代了*
void fun(M m1,M & m2);
3.this 指针
this 是一个由系统自动提供的指向对象的特殊指针。 该镇值是一个指向 正在对某个成员函数操作的对象的指针。
当对一个对象调用成员函数时,编译器先将该对象的地址赋值给系统创建的this指针。然后再调用成员函数,每次成员函数存取数据成员时,都会隐含着使用this指针。同样适用*this 来标识 调用该成员函数的对象。
案例:6.4
//example 6.4 吕
#include <iostream>
using namespace std;
class A
{
public:
A(){
a=b=0;
}
A(int i,int j)
{
a=i;b=j;
}
void copy(A &aa);//其中的&为引用
void print()
{
cout<<a<<","<<b<<"\n";
}
private:
int a,b;
};
void A::copy(A &aa)
{
if(this==&aa) return; //其中的&为取地址运算符 成员函数中的this的值为aa对象的地址
* this=aa; //将aa对象中的成员值 赋值给this指向的对象 ,对象间的赋值
}
int main()
{
A a1,a2(3,4); //初始化两个对象
a1.copy(a2); //用a2来赋值a1
a1.print();
return 0;
}
6.4对象数组和对象指针数组
1.对象数组
数组中元素为对象的数组 需要都是同一个类的对象
类名 数组名 [大小]
DATE dates [10]; 一维数组
DATE dates [10][5]; 二维数组
2.对象数组的赋值
对象数组可以赋初值 也可以赋值
DATE(int m,int d,int y), //构造函数
DATE dates[3]={DATE(1,2,3),DATE(4,5,6),DATE(7,8,9)}; 初始化
dates[0]=DATE(7,22,1998); 对数组中的某个成员赋值
案例:
//example 6.5 吕
#include <iostream>
using namespace std;
class DATE
{
public:
DATE()
{
month=day=year=0;
cout<<"default called.\n";
}
DATE(int m,int d,int y)
{
month=m;
day=d;
year=y;
cout<<"constructor called\n";
}
~DATE() //析构函数
{
cout<<"destructor called.\n";
}
void print()
{
cout<<"month="<<month<<";day="<<day<<";year="<<year<<"\n";
}
private:
int month,day,year;
};
int main()
{
DATE dates[5]={DATE(7,22,1998),DATE(7,23,1998),DATE(20,11,2003)}; //头三个元素用非默认构造函数初始化;后两个用默认的构造函数初始化
dates[3]=DATE(7,25,1998); //非默认构造函数来更改数组元素的值
dates[4]=DATE(1,7,2003); //同上
for(int i(0);i<5;i++)
dates[i].print();
return 0;
}
上面的dates[3] 先用构造函数创建一个无名的对象,然后将它赋值给数组元素 ,然后再将无名对象通过调用析构函数来实现。
6.2指向数组的指针和指针数组
1.指向数组的指针
类型说明符 (* 指针名) [大小]
int (* P)[3]; // int型 3个元素组成的数组 p为指针 指向这个数组
类名 (* 指针名) [大小]
类名 (* PL)[4]; //PL为指针名 指向包含4个对象元素的数组
//example 6.6
#include <iostream>
using namespace std;
int a[][3]={1,2,3,4,5,6,7,8,9};//列表初始化
int main()
{
int (*pa)[3](a);//声明数组包含3个元素 每个元素为指向整型的指针类型 初始化pa为a a为二维数组的地址
for(int i=0;i<3;i++)
{
cout<<"\n";
for(int j=0;j<3;j++)
cout<<*(*(pa+i)+j)<<""; //*pa 第一次解引用 从二维数组降到一维数组 *(*pa) 从一维降到数组中的单个元素 所以输出的是元素的值
}
cout<<"\n";
return 0;
}
pa为指向数组a的第0行一维数组的指针
注意:指向一维数组的指针都用二维数组的某个行地址(即该行首地址)赋值。
6.7 指向对象数组的指针
//exmaple 6.7
#include <iostream>
using namespace std;
class M
{
public:
M(){
a=b=0;
}
M(int i,int j)
{
a=i;b=j;
}
void print()
{
cout<<a<<","<<b<<'\t';
}
private:
int a,b;
};
int main()
{
M m[2][4]; //二维数组 元素为M类对象 数组名为m
int x=10,y=10;
for(int i=0;i<2;i++)
for(int j=0;j<4;j++)
m[i][j]=M(x+=2,y+=10); //初始化 对象数组
M(*pm)[4](m);// //声明 指向数组的指针 指针指向的数组元素为M类的对象 二维数组名代表地址 赋值给pm[0]
//定义一个指针 指向对象数组的指针pm 用二维m的数组名来初始化 pm指向二维数组m的首行
for(int i=0;i<2;i++)
{
cout<<"\n";
for(int j=0;j<4;j++)
(*(*(pm+i)+j)).print(); //两次解引用 从二维变为一维 再从一维变为数组中的元素 M类的对象
}
cout<<"\n";
return 0;
}
2.指针数组
数组元素为指针的数组称为指针数组。一个数组的元素可以是指向同一个类型的一般指针,也可以是指向同一类类型的对象指针
类型名 * 数组名 [大小]
表示该数组名中的元素为指针
int * pa[3]; //包含3个元素 每个元素是一个int型的指针
char * pa[2][5]; //包含10个元素 每个元素是一个char型的指针
//example 6.8 吕 指针数组 元素为指针
#include <iostream>
#include <string.h> //此时需要加.h 没有h 编译器找不到函数
using namespace std;
const int N=5; //全局常量
int main()
{
char * strings[N]; //声明一个指针数组 元素为指向字符串的指针
char str[80]; //声明一个字符串数组
cout<<"At each prompt,enter a string:\n";
for(int i=0;i<N;i++)
{
cout<<"Enter a string #"<<i<<":";
cin.getline(str,sizeof(str)); //获取一行输入中 实际输入长度字符串的首地址 赋值给str
strings[i]=new char[strlen(str)+1]; //开辟一个长度比输入的字符串长度+1的数组 将首地址赋值给指针数组的对应元素(字符串指针)
strcpy(strings[i],str); //将输入进来的字符串赋值拷贝到新开辟的位置的数组,并且通过字符串指针来访问呢
}
cout<<endl;
for(int i=0;i<N;i++)
cout<<"string #"<<i<<":"<<strings[i]<<endl; //输出指针数组中各个元素指向的字符串
return 0;
}
所有数组元素都是指向同一个类类型的对象的指针
类名 * 数组名 [大小]
//example 6.9 吕
#include <iostream>
using namespace std;
class A
{
public:
A(int i=0,int j=0){
a=i;b=j;
}
void print();//声明带分号 实现不用分号
private:
int a,b;
};//类的声明 加上分号
void A::print()
{
cout<<"a="<<a<<";"<<"b="<<b<<"\n";
}
int main()
{
A a1(7,8),a2,a3(5,7); //调用不同的构造函数 初始化
A *b[3]={&a3,&a2,&a1}; //列表初始化指针 指针数组
for(int i=0;i<3;i++) //
b[i]->print();
}
6.2 带参数的main函数
void main(int argc,char * argv[])
其中 argc是代表命令行参数的个数 (包括命令本身也计数) argv 数组包含参数的实际内容 命令本身也包含在内
argv[0] 命令字
argv[1] 命令行第一个参数
argv[2] 命令行第二个参数
...
//example 6.10
#include <iostream>
using namespace std;
int main(int argc,char * argv[])
{
cout<<"the number of command line arguments is"<<argc<<endl;
cout<<"the program name is "<<argv[0]<<endl; //打印程序名
if(argc>1)
{
cout<<"the command line arguments:\n"; //
for(int i=0;i<argc;i++)
cout<<argv[i]<<endl; // 打印从1开始的参数内容
}
return 0;
}
6.3常类型
用const来说明 对象或者变量的值是不能被更新的 所以定义或说明常类型量时必须进行初始化
int const m=15;
m=18; //此语句错误 因为前面已经初始化了m值 不能再修改
6.3.1 一般常量和对象常量
int const x=2; //const 可以如此 也可以位于int之前
类型说明符 cosnt 数组名 [大小]=初始值
const 类型说明符 数组名 [大小]=初始值
int const a[5]={1,2,3,4,5};//数组元素的值是不能被更新的。
2.常对象
常对象是指对象常量,定义格式:
类名 const 对象名 (初始值)
初始化后对象不能被更新 同样const的位置可以变化
6.3.2 常指针和常引用
1.常指针
常指针使用修饰符const说明 一种表示指针的地址值是常量 另一种表示指针所指向的是常量 格式不同
类型 * const 指针名=初始值 表示指针是常量 即 指针的值 (实际的地址) 不可以被修改 ;但是指向(实际内存地址处)的值可以被修改。
char * const ps1=s1; ps1的值是不变的 始终为s1 *ps1 可以改变
ps1=s2 是非法的
* ps1=“char” 指针所指向的值是可以改变的 将char 字符串存入ps1的地址处
2.所指向的值是常量的常指针
这是一种指向某类型常量的指针 它定义的格式如下:
const 类型 * 指针名=初始值
const char * ps2=s2;
ps2 是一个常指针 指向的量是常量 不能改变 但是该指针的地址值是可以改变的
3.常引用
使用const修饰符可以说明引用 被说明的引用为常引用 该引用所引用的对象不能被更新。
const 类型说明符 & 引用名= 初始值
double b(1,2);
const double & v=b;
v=a; //此语句非法
c++中 常指针和常引用往往用来作为函数的形参,这样的参数称为常参数。使用const修饰的常指针和常引用更多
好处是参数传递过程中不必执行复制构造函数 会改善程序的运行效率
//6.11 example const pointer as argument
#include <iostream>
using namespace std;
const int N=5; //const修饰的常量
void print(const int * p,int n);//函数原型
int main(int argc,char * argv[])
{
int array[N];
for(int i=0;i<N;i++)
cin>>array[i]; //将标准输入到数组中 以单个字符输入
print(array,N); //打印数组中的各个元素 //传递实参
return 0;
}
void print(const int *p,int n) //函数实现 //形参接受 注意接收的方式
{
cout<<"{"<<*p<<endl; //打印数组中的第0个元素
for(int i=1;i<n;i++)
cout<<","<<*(p+i)<<endl;//打印数组中其他的元素
cout<<"}"<<"\n";
}
//6.12 常引用作为函数的参数
c++ 吕凤翥 第六章 类和对象(二)的更多相关文章
- c++ 吕凤翥 第五章 类对象一
一 类的声明和实现 1. class tdate //声明部分 { public: void setdate(int y,int m,int d); int isleapyear(); voi ...
- 《深入理解java虚拟机》第六章 类文件结构
第六章 类文件结构 6.2 无关性的基石 各种不同平台的虚拟机与所有的平台都统一使用的程序存储格式--字节码(ByteCode)是构成平台无关性的基石.java虚拟机不和包括java在内的任何语言 ...
- JVM学习笔记-第六章-类文件结构
JVM学习笔记-第六章-类文件结构 6.3 Class类文件的结构 本章中,笔者只是通俗地将任意一个有效的类或接口锁应当满足的格式称为"Class文件格式",实际上它完全不需要以磁 ...
- JAVA入门第二季 第一章 类和对象
面向对象编程 Object Oriented Programming OOP 第一.什么是类和对象 在具体说明类和对象之前,先说说别的. 眼睛在人类身体上最为有用的器官.如果一个没有了眼睛,这个人与世 ...
- 第六章:Javascript对象
对象是javascript的基本数据类型.对象是一种复合值.它将很多值(原始值 或者其他对象)聚合在一起.可通过名字访问这些值.对象也可以看做是属性的无序集合,每个属性都有一个名/值.属性名是字符串, ...
- JAVA基础第三章-类与对象、抽象类、接口
业内经常说的一句话是不要重复造轮子,但是有时候,只有自己造一个轮子了,才会深刻明白什么样的轮子适合山路,什么样的轮子适合平地! 我将会持续更新java基础知识,欢迎关注. 往期章节: JAVA基础第一 ...
- java面向对象编程——第四章 类和对象
OO:面向对象 OOP:面向对象编程 OOA:面向对象分析 OOD:面向对象设计 结构化编程:从顶向下,将一个大问题分解成更小的任务,然后为每一个更小的任务编写一个过程.最后程序员会编写一个主过程来启 ...
- Java入门第二季第一章类和对象知识点
Java 中的 static 使用之静态方法 1. 静态方法中可以直接调用同类中的静态成员,但不能直接调用非静态成员.如: 如果希望在静态方法中调用非静态变量,可以通过创建类的对象,然后通过对象来访问 ...
- Java入门篇(六)——类和对象
写到这里终于写到了入门篇的最后一个知识点了.类和对象是Java中经常被提到的两个词汇,实际上可以将类看作对象的载体,它定义了对象所具有的功能.Java是面向对象的语言,因此掌握类与对象是学习Java语 ...
随机推荐
- sql队伍的胜负情况
1.数据显示情况 2.sql语句执行情况 USE [数据库名] GO /****** Object: Table [dbo].[测试] Script Date: 08/03/2017 10:58:02 ...
- linux服务器免密钥登录
方法一:通过下载服务器私钥方式 服务器端执行: ssh-keygen -t rsa -b 4096 cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys ...
- ssh免密钥登录一例问题
今天遇到一个奇怪的问题,在同一机器上创建的普通用户使用 ssh-copy-id -i .ssh/id_rsa.pub root@192.168.3.254 建立与root用户的免密钥通信,结果死活还是 ...
- pat甲级1020中序后序求层序
1020 Tree Traversals (25)(25 分) Suppose that all the keys in a binary tree are distinct positive int ...
- JavaScript and Ruby in ABAP
Netweaver里有个mini JavaScript engine CL_JAVA_SCRIPT, 对于Js code的编译和执行都是用system call完成. 只能当玩具用:report SJ ...
- vuejs数据和事件
<body> <div id='root'>{{number}}</div> <script> new Vue({ el:'#root', data:{ ...
- Java设计模式学习——简单工厂
一. 定义与类型 定义:有工程对象决定创建出哪一种产品类的实例 类型:创建型,但不属于GOF23中设计模式 二. 适用场景 工厂类负责创建的对象比较少 客户端(应用层)只知道传入工厂类的参数,对于如何 ...
- js常见问题总结归纳
一.使用 typeof bar === "object" 来确定 bar 是否是对象的潜在陷阱是什么?如何避免这个陷阱? 首先typeof bar === "object ...
- SAP系统管理中常见问题解答(转载)
1.如何查看SAP系统的位数? system——status看 Platform ID Platform 32-bit 64-bit --------------------------------- ...
- vim粘贴取消自动缩进
Vim 复制粘贴探秘 Vim 作为最好用的文本编辑器之一,使用vim来编文档,写代码实在是很惬意的事情.每当学会了vim的一个新功能,就会很大地提高工作效率.有人使用vim几 十年,还没有完全掌握vi ...