类和对象

类和对象
一、知识点总结
6.1
对象的所属类型成为类,类是对一组具有相同属性和行为的对象的抽象。
C++能够定义抽象数据类型,最常见的方式是采用结构体加全局函数。结构体描述数据,全局函数描述对这些数据的操作,数据则以参数的形式传递给函数。
6.1.2数据成员与成员函数
结构体内的函数被称为成员函数,结构体中的数据则称为数据成员。这样的结构体被称为类,这种结构体类型的变量被称为对象。
程序6.1
//数据成员声明
struct SalesData{
//数据成员声明
string productNo;//productNo被初始化为默认值空串
double price;
unsigned unitSold; //price和unitSold是未定义的
//成员函数声明和定义
double totalRevenue(){ return priceunitSold;}
void read(){cin>>productNo>>price>>unitSold;}
void print(){cout<<productNo<<":"<<price<<" “<<unitSold<<” “<<totalRevenue()<<endl;}
}; //结构体定义结束
在这个SalesData类定义中,将进行销售总值计算、输入和打印输出的函数放入了结构体内部,成为结构体的成员函数。由于结构体成员的名字不会与全局名字发生冲突,因而避免了函数名字冲突的可能性。
成员函数像数据成员一样,不能独立使用,必须由结构体类型的变量使用成员选择语法来进行调用。而实施调用的这个结构体变量就是成员函数的操作对象。
6.1.3数据成员类内初始化
数据成员类内初始化只能放在等号”=“右边,或放在”{}“里,不能使用圆括号”()"。
6.1.4成员函数类外定义
成员函数在内外定义时,函数名字前面要加类名字和作用域符"::",表示这个函数是在其所属的类作用域内,是这个类的成员函数,不同于全局函数。
如果要在类外定义inline成员函数,需要显示的在成员函数声明或定义前加关键字inline。
★成员函数在类内和类外定义有什么不同吗?
在类定义的花括号内定义的成员函数默认为inline 函数
在类外定义inline 成员函数
显式地在函数声明或定义前加关键字inline
6.1.6包含守卫
可以使用预处理器指令防止类定义的多次包含,这些语句称为包含守卫。使用包含守卫的头文件格式如下:
#ifndef HEADER_FlAG
#define HEADER_FlAG
#endif
6.2访问控制和封装
6.2.2访问限定符
struct类名{
public:
公有成员声明;
private:
私有成员声明;
protected:
被保护成员声明;
}
public 成员在程序的任何函数或类中都可以被访问
用于说明类接口中的成员,客户程序通过public 成员可以操纵该类型的对象
private 成员只能由类自己的成员函数或友元访问
需要隐藏的信息应该声明为private
protected 成员可以在派生类中访问
访问权限介于public 和private 之间,主要用于继承中
protected 成员可以由类自己的成员函数、友元、派生类成员访问
如果没有指定访问限定符,struct 成员的默认访问限制为public
★ 公有成员在任何地方都可以使用,使用方法用成员名加点,私有成员只能在类内使用。数据成员一般是私有的,成员函数一般是共有的。
6.2.3类和对象
将数据和操作捆绑在一起,并加上访问控制,这在面向对象中称为封装。
对象是客观事物的抽象,类是一组具有相同属性和行为的对象的抽象,对象又称类的实例。
★面向对象编程的主要工作就是创建一组对象并给它们发送消息。
▲class 和struct 定义的类稍有区别
如果class的成员没有设置访问限定符,则默认为private;而struct成员的默认访问限定是public。
SalesData类可以用class关键字声明如下:
class SalesData{
public: //类的接口
double totalRevenue();
void read();
void print();
private: //私有数据
std::string productNo;
double price = 0.0;
unsigned unitSold = 0;
};
sizeof运算符可以用于类类型、对象和类的数据成员,例如:
SalesData sd;
sizeof(SalesData);
//SalesData 类型的对象所占空间大大小
sizeof sd;
//sd 的类型的大小,即sizeof(SalesData)
sizeof(SalesData::price);
//获取类成员的大小,无需具体的对象
union关键字也可以用来定义类,和struct一样,其成员的默认访问限定是public,但其数据成员的存储方式不同于class和struct定义的类:union类的每个成员都是从对象的首地址开始存放,所以同一时间只有一个数据成员有效。
6.2.4this指针
每个成员函数都有一个隐含的参数,指向接收消息的对象,称为this指针
X类的this指针的类型是X

this指针是一个常量,含有当前实施调用的对象的地址
不能改变this指针的值,也不能取this指针的地址
▲▲▲
class X {
int m;
public:
void setVal(int m) { this -> m = m; }
X add(const X& a) {
m += a.m;
return this; //返回当前对象
}
void copy(const X& a) { //复制对象
if (this == &a) //同一对象,无需复制
return;
m = a.m; //拷贝复制
}
};
6.2.6友元
C++引入了friend关键字,如果想让非成员函数访问一个类中的私有数据,应该在类中将这个函数声明为friend(友元)。例如:
class Complex{

friend Complex add (const Complex&,const Complex&);
}
一个类的友元可以访问该类的私有数据
在声明友元时要遵循一条规则:友元必须在被访问的类中声明
friend关系不可传递:
如果A是类B的友元,而类B是类C的友元,A不会自动成为C的友元。friend关系是不可继承的:基类的友元不会自动成为其派生类的友元。
应该尽量避免使用友元(最后是只在运算符重载时使用友元)。
友元会破坏类的封装性,增加类之间的耦合度。
6.3构造函数和析构函数
构造函数在创建对象时被自动调用,负责初始化对象。构造函数的名字和类名字相同,它没有返回类型(是没有返回类型,不是返回void 类型)。构造函数的参数通常为数据成员提供初始值
构造函数可以重载
构造函数的形式决定了初始化对象的方式
6.3.2构造函数初始化列表
成员1(初始值1)[,成员2(初始值2),…]
初始化列表位于构造函数的参数表之后,函数体之前:
构造函数(参数表) : 初始化列表 { 函数体 }
▲▲▲在初始化列表中,每个成员只能出现一次,成员初始化的顺序与他们在类定义中出现的顺序一致。构造函数初始化列表中初始值的先后顺序不会影响实际的初始化顺序。
▲class X{
int a, b; //成员声明顺序
public:
X(int val):a(val), b(a){} //先初始化a,再用a初始化b
};
//下面的形式更好一些: 用val 初始化,没有成员的依赖
X(int val) : a(val), b(val){}
//下面的形式可能会产生未定义的行为:试图用未定义的值b初始化a
X(int val) : b(val), a(b){} //编译器会警告
6.3.4析构函数
析构函数负责在对象生存期结束时返回相关资源和自动释放资源。当对象离开作用域时,或者用delete 释放在堆上创建的对象时,析构函数都会被自动调用。
析构函数的名字是类名字前加波浪线“~”。析构函数没有返回类型,也没有任何参数。析构函数不能重载,只能为一个类定义唯一一个析构函数。
▲注意:程序中无论以何种形式创建对象,都会调用构造函数,各种初始化方式都会引起相应的构造函数调用。堆上的对象,数组元素对象都要调用构造函数初始化。当对象生命期结束时,则会调用析构函数。析构函数绝大多数情况下都被自动地隐式调用,一般不要显式调用(程序6.11 析构函数的不当调用)
6.4const数据成员
声明const成员函数的语法形式:
返回类型 成员函数名(参数表) const;
定义const成员函数的语法形式为:
返回类型 成员函数名(参数表) const {函数体}
注意const成员函数声明和定义的语法
对于在类外定义的const成员函数,必须在它的定义和声明中同时指定关键字const
只有声明为const 的成员函数才可以被const 对象调用,const 对象不能调用非const 成员函数,非const 对象可以调用const 成员函数,const 成员函数中不能修改类的数据成员,const 成员函数中不能调用其他非const 成员函数。
X 类的成员函数的隐含参数this 指针是X
类型,const X 对象的地址是const X类型,const对象调用非const成员函数时,形参this是X类型,实参即当前对象的地址是const X类型。编译器不能将实参const X类型转换为形参类型X*,报告错误:参数类型不匹配。
6.5 .1static数据成员
▲static数据成员特点:属于整个类,但不专属于某个对象!因而不能在构造函数中初始化。
静态成员可以是private成员,而全局变量不能。在类的数据成员声明前加关键字static,就使该数据成员成为静态的。static成员仍然遵循访问控制规则。
static数据成员在类定义之外初始化,使用类名字限定
int Object::count = 0;
static成员只能定义一次,定义一般不放在头文件中,而是放在包含函数定义的源文件中。
⑴类中的常量——static const成员
和static成员一样,static const数据成员的初始化也是在类外,类外定义时要加const关键字。
⑵在非成员函数中通过两种方式访问static数据成员:
成员访问运算符“.”或“->”,像访问普通数据成员的语法一样,通过对象或指针来访问;
类名限定的静态成员名:static成员只有一个副本,可以直接用类名字限定的静态成员名字访问 ClassName::StaticMemberName
注意:static成员仍然遵循访问控制的约束
6.5.2 static成员函数
声明语法是在类定义中的函数声明前加static 关键字,在类外定义时不需要加关键字static。
static成员函数没有this指针,在static成员函数中显式或隐式地引用this指针都会引起编译错误
6.6指向成员的指针
使用数据成员指针的语法
如果有一个指向类成员的指针p,要取得指针指向的内容,必须用“”运算符解引用。p是一个类的成员,不能直接使用,必须指定对象或指向对象的指针来选择这个成员
使用指向数据成员的指针的语法应该如下:
对象.
指向成员的指针
对象指针 ->
指向成员的指针
指向成员的指针的定义语法如下
成员类型 类名::*指向成员的指针;
struct Simple { int a; };
int main() {
Simple so, *sp = &so;
sp->a;
so.a;
//如果pa是指向Simple成员a的指针
//通过对象so选择pa指向的成员:
so.*pa;
//通过指针sp选择pa指向的成员:
sp->*pa;
}
定义指向成员函数的指针时也要指明成员函数的类类型。基本语法: 返回类型(类名::*指向成员函数的指针)(参数表);
//成员函数指针的定义和使用语法
class Simple {
public:
int f(float) const { return 1; }
};
int (Simple::*fp)(float) const;
int (Simple::*fp2)(float) const = &Simple::f;
int main() {
fp = &Simple::f; //获取成员函数的地址必须使用取地址符
}
二、感悟
从开始学习指针到学习类与对象,对我来说都是很不顺利,因为有很多抽象的概念需要理解和记忆。一节知识点需要反复去看、反复理解才能慢慢地掌握,花费了很多时间和精力,但是效率很低,效果不太好,不知道该怎样应用,做作业时不知道从哪里着手。后来又看了一些代码,结合知识点,看这些知识是如何被运用的,后来就慢慢地掌握了一些。
学习就是如此,我们只能学会我们想学会的东西,只有真正的想学并付出努力,我们才能收获知识、提高能力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值