类与对象(一)《C++ Primer》笔记

一、类定义

类是C++中最基本的特性之一,是一种用于定义自己的数据结构及其相关操作的机制,类可以包含数据,函数和类型成员。一个类定义一种新的类型和一个新的作用域。

类定义包括两部分。类头,由关键字class及其后面的类名构成。类体,被一对花括号包围,类体中定义了类成员表。类定义后面必须接一个分号或一列声明。

class Test { /* ... */ }; 
class Test { /* ... */ } myclass;

注意:即使两个类类型具有完全相同的成员表,它们仍是不同的类型。

在引入类类型之后 我们可以以两种方式引用这种类类型
1 指定关键字 class 后面紧跟类名。

class Test obj1;

2 只指定类名。

Test obj2;

这两种引用类类型的方式是等价的。第一种方式是从 C 中借用的,在 C++中也是有效的,第二种方式是 C++引入的,它使类类型更容易被用在声明中。

扩展
1.C的结构体和C++结构体的区别
(1) C的结构体内不允许有函数存在,C++允许有内部成员函数,且允许该函数是虚函数。所以C的结构体是没有构造函数、析构函数、和this指针的。
(2)C的结构体对内部成员变量的访问权限只能是public,而C++允许public,protected,private三种。
(3)C语言的结构体是不可以继承的,C++的结构体是可以从其他的结构体或者类继承过来的。
2.C++的结构体和C++类的区别
(1)C++结构体内部成员变量及成员函数默认的访问级别是public(因为要兼容C),而c++类的内部成员变量及成员函数的默认访问级别是private。
(2)C++结构体的继承默认是public,而c++类的继承默认是private。

1.数据成员

类数据成员可以是任意类型(除了自身类型),且它们的声明方式同变量声明相同,类也可以有静态(static)数据,且有特殊的属性,类的静态成员将在《类与对象(二)》中介绍。

注意:数据成员虽不能被指定为自身类型,但是可以为指向自身类型的指针或引用。

class Test { 
 Test sc1; // error
 Test *sc2; // ok
 Test &sc3; // ok
};

当实例化一个类的对象时,编译器才会根据类的定义来分配相应的存储空间。因此在创建对象前,一定要有完整的类定义,这样编译器才能正确计算所需空间。上述代码中,由于Test类还没有定义结束,在内部定义一个Test类型的对象时,编译器无法知道该为sc1分配多少空间,因此报错。而指针和引用所占存储空间大小与类型无关,编译器可以计算分配空间,所以可以编译通过。

注意:除了静态 static 数据成员外 数据成员不能在类体中被显式地初始化。

class Test { 
 int a = 0; // error
 double b = 0.0; // error
};

类的数据成员通过类的构造函数进行初始化,关于类的构造函数我们将在《类与对象(三)》讨论。

2.成员函数

类的成员函数的声明放在类体中,而定义既可以放在类体中,也可以放在类体外。

//声明与定义全部放到类体中
class Test {
 int Add (int* a, int* b){
  return (a+b);
  }
};
//声明放在类的头文件Test.h中                           
class Test {                                        
 int Add (int* a, int* b);                           
 }                                                 
//定义放在类的实现文件Test.cpp中
#include "Test.h" 
int Test::Add (int* a,int* b){     //Add被它的类名限定修饰
 return(a+b);                               
}     

注意:成员函数如果在类体中定义,编译器会将其当做内联函数来处理,故一般情况下更建议用上图第二种方法定义。

  • 成员函数拥有访问该类的公有和私有成员的特权,无需使用点或箭头成员访问操作符。
  • 成员函数可以是重载的函数,但是一个成员函数只能重载自己类的其他成员函数。

3.成员访问及封装

成员访问

  • 关键字publicprivateprotected 被称为访问限定符。
  • 一个类可以包含多个public、 private、 protected区。每个区一直有效,直到另一个区标签或类体的结束右括号出现为止。如果没有指定访问限定符,则缺省情况下,在类体的开始左括号后面的区是 private 区。
  • 在public区内被声明的成员是公有成员,在private区内被声明的成员是私有成员,在protected 区域内被声明的成员是被保护成员
  • 公有成员在程序的任何地方都可以被访问,私有成员只能被成员函数和类的友元访问,被保护成员对派生类就像 public 成员一样对其他程序则表现得像 private。

注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别。

封装
C++面向对象的三大特性:封装继承多态
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

4.友元

友元函数

  • 友元机制允许一个类授权其他的函数访问它的非公有成员。
  • 友元函数定义在类外。但也可定义在类内,这时的友元函数是隐式内联的。
  • 友元函数需要在授权类的内部声明,声明时须加friend关键字,且在类内出现的具体位置不受限(最好在类定义开始或结束的位置集中声明友元)。
class Test { 
 friend int Add ( int* a, int* b);  
 public: 
  int a;
  int b;
 // ... Test 类的其他部分
};
int Add (int* a,int* b){
 return(a+b);
 }
  • 友元函数不属于授权类,并不受它所在区域访问控制级别的约束。
  • 当把一个类的成员函数声明为友元时,必须指出该成员函数属于那个类。
  • 友元的声明仅仅指定了访问权限,而非一个通常意义上的函数声明。部分编译器强制限定我们必须在友元声明之外再专门对函数进行一次声明(甚至即使我们在授权类的内部定义了该函数)。
  • 友元函数不能用const修饰。
  • 一个函数可以是多个类的友元函数。

友元类

  • 友元类的所有成员函数都可以是另一个类的友元函数。
  • 友元关系是单向的,不具有交换性。A类声明B类为其友元类,那可在B类中访问A类的私有成员 ,A类则不可以访问B类的。
  • 友元关系不能传递。若B为A的友元,C为B的友元,不能说C为A的友元。

注意:友元提供了一种突破封锁的方法,有时提供了便利,但是友元会增加耦合度,破环了封装,所以友元不宜多用。

二、类对象

  • 类的定义不会引起存储区分配 只有当实例化一个类的对象时,系统才会分配存储区。

类对象的大小
1.实例化一个类的对象时,类对象只保存了数据成员,而成员函数则放在公共代码段供所有该类的对象调用。一个类对象的大小,实际为该类所有数据成员内存对齐后的大小。
2.注意:空类比较特殊,编译器给了空类一个字节来唯一标识这个类
内存对齐的规则
1.第一个成员在与结构体偏移量为0的地址处。
2.其他成员变量要对齐到对齐数的整数倍的地址处。对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。VS中默认的对齐数为8,gcc中的对齐数为4,使用

pragma pack (n) 

可以修改默认对齐数为n,n值只能被设置为1,2,4,8,16.
3.结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

  • 一个对象可以被同一类类型的另一个对象初始化或赋值
class Test;
class Myclass = Test;
  • 我们必须用成员访问操作符来访问类对象的数据成员或成员函数,点成员访问操作符(.)与类对象或引用联用,箭头访问操作符(->)与类对象的指针联用.
class Test;
class* s1 = Test;
class& s2 = Test;
Test.Add();
s1->Add();
s2.Add();

三、隐含的this指针

如前文所说,类的成员函数存放在公共代码段,供各个该类类型的对象调用,且成员函数可以引用自己的类成员而无需使用成员访问操作符,那么成员函数是如何保证要正确引用调用它的对象的数据成员呢?答案便是this指针,如下图。

void Display(){       //Display为Test类的一个成员函数
 cout <<_a << endl;   //_a为类Test的一个数据成员
 }
void Display(Test* this){    //编译器自动处理成员函数隐式的this指针,
 cout <<this->_a << endl;   
 }
  • 每个类成员函数都含有一个指向被调用对象的指针,这个指针被称为this。
  • 它本质上是一个成员函数的形参,对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
  • this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
  • 在非const成员函数中,它的类型是指向该类类型的指针,在const成员函数中,是指向const类类型的指针。(const成员函数将在《类与对象(二)》介绍)。
  • this指针可以为空。

this指针的应用举例

  1. 当多个成员函数应用到同一个对象上时,我们可以将成员函数调用连接起来,此时,this指针是返回该成员函数时被应用的对象,这些成员函数的返回类型也必须改变成:类名&
Test& Test::Add(){
 //函数体
 return *this;
 }
  1. 判断调用拷贝的对象是否为要被拷贝的对象
void Test::copy( const Test& sobj ) 
{ 
 // 如果调用该函数的对象与 sobj 是同一个对象
 // 无需拷贝
 if ( this != &sobj ) 
 { 
 // 把 sobj 的值拷贝到 *this 中
 } 
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值