自学内容网 自学内容网

类与对象(二)

类与对象的定义与访问

面向对象的方法支持继承、多态机制

定义类和对象

类由成员构成:
数据成员——描述对象的属性
成员函数——描述对象的方法
介绍了几类不同的访问特性。
对象是类类型的变量,说明方法与普通变量相同。说明一个类类型的对象之后,编译器为每个对象的数据成员分配内存,对象没有成员函数的副本(相对于普通变量来说,分配的是内存,对于函数来说就是建立副本,他俩意思其实差不多,只不过是针对他俩另说),类成员函数可以被对象调用。
定义变量时要分配存储空间,同样,定义一个对象时要分配存储空间,一个对象所占的内存空间是类的数据成员所占的空间总和。局部变量在栈区(系统自动分配和释放内存),堆区是人为用malloc申请的,最后用free()函数释放,静态和全局的在静态区,类的成员函数存放在代码区
在这里插入图片描述

//类中定义成员函数内联函数处理

内联函数补充:
内联扩展是用来消除函数调用时的时间开销。
它通常用于频繁执行的函数,对于小内存空间的函数非常受益。
注意:
1.递归函数不能定义为内联函数

2.内联函数一般适合于不存在whileswitch等复杂的结构且只有1~5条语句的小函数上,否则编译系统将该函数视为普通函数。

3.内联函数只能先定义后使用,否则编译系统也会把它认为是普通函数。

4.对内联函数不能进行异常的接口声明。

访问对象成员

格式
类外用 “ . ” 和 “ -> ” 运算符访问对象成员

1.用“.”访问的略
2.用“->”访问
class a
{
public:
a();
~a();
int x, y;
void print();
};
a::a()
{
cout << "contructed" << endl;
}
a::~a()
{
cout << "deleted" << endl;
}
void a::print()
{
cout << x << endl << y << endl;
}
int main()
{
a* b1 = new(a);
b1->x = 3;
b1->y = 4;
b1->print();
delete b1;
return 0;
}

对象指针

class a
{
public:
a();
~a();
int x, y;
void print();
};
a::a()
{
cout << "contructed" << endl;
}
a::~a()
{
cout << "deleted" << endl;
}
void a::print()
{
cout << x << endl << y << endl;
}
int main()
{
a c1;
a* c2;
c2 = &c1;
c2->x = 1;
c2->y = 2;
c2->print();
//华丽丽的分界线
a* b1 = new(a);
b1->x = 3;
b1->y = 4;
b1->print();
delete b1;
return 0;
}

this指针

由于系统不会为每个类的对象建立成员函数副本的缘故,但是类的对象又得调用成员函数。因此,C++提供了一个称为this的隐含指针参数,方便知道在哪个对象上操作,在这个对象调用成员函数的时候,这个对象的地址就已经被传递给this指针。同时this指针不能显示说明,但可以显示使用。例如:
(*this).a=a或者this->a=a;用于形参和数据成员重名的时候用,当然也可以用作用域::来写,一些用成员函数重载运算符的时候也会用到。
上述加粗的地方解释为:
在这里插入图片描述

 void setXY ( int a, int b)
  { this-> a = a ;  this-> b = b ; }
  或者写成:
  class A
  {
   void setXY ( int a, int b)
  { A:: a = a ;  A::b = b ; }
  };

this指针是个常指针。(目前没遇到相关题目)

不能显示说明,可以显示使用。
你写的时候长这样:
 void setXY ( int a, int b) { x = a ;  y = b ; }
 实际上,在系统看来,是这样的:
 void setXY ( int a, int b, Simple * const this )
  { this->x = a ;  this->y = b ; }//这一块也可以显示使用
  //主函数里
  obj1 . setXY ( 10, 15, &obj1 ) ;
  this指针传地址

对象数组

student stu[10]
用着和平常的对象没啥区别,也就是对象的个数多了而已。

对象用作函数的参数和返回值

  1. 直接用对象作为参数,调用函数时,进行值传递,实参的值要复制给形参,如果类的数据成员较多时,需要一一复制.
class a
{
public:
a();
~a();
int x, y;
void ctrl(a a1)
{
x = a1.x;
y = a1.y;
}
void print();
};
  1. 但是,这种参数传递方式效率不高,可以使用对象指针或对象引用方式来传递函数参数。
    (?)还没遇到。。。

构造函数和析构函数

对于一般的数据类型,一整套流程的分配内存、数据初始化、内存回收的工作,编译程序能够很轻易的完成。
但是对于类类型的对象来说,虽然C++提供了默认版本的构造和析构函数。但是由于类体系结构的复杂性,建立对象的初始化工作和释放对象资源的变化很大,所以通常需要用户自己自定义进行。

class student
{ public:
student(int,char*,int,float);
~student();
void print();
 private:
int id;
char* name;
int age;
float score;
};
student::student(int i, char* c, int a, float s)
{
cout << "constructing…" << endl;
id = i;
age = a;
score = s;
name = new char[strlen(c) + 1];//这儿申请了一个数组,动态申请建议回顾
if (name != 0)
strcpy(name, c);
}
student::~student()
{
cout << "destructing…" << endl;
delete [] name;//非常必要
}
void student::print()
{
cout << "学号:" << id << "姓名" << name;
cout << " "<< age << " " << score << endl;
}
int main()
{
student stu(1, "wang", 18, 86);
stu.print();
return 0;
}

简单构造函数和析构函数

构造函数是用于创建对象的特殊成员函数
当创建对象时,系统自动调用构造函数
构造函数的作用是:
为对象分配空间;对数据成员赋初值;请求其他资源
没有用户定义的构造函数时,系统提供缺省版本的构造函数
构造函数名与类名相同:类名
构造函数可以重载
构造函数可以有任意类型的参数,但没有返回类型
构造函数必须是公有的

要求:

1、使用score类,输入某班n个(事先不确定)学生的学号和各科成绩,列表输出学生的学号、各科成绩和平均成绩

2、定义静态成员,用于记录对象的个数,每创建一个对象,对象个数增1,每撤销一个对象,对象个数减1,输出对象的个数
//1、使用score类,输入某班n个(事先不确定)学生的学号和各科成绩,列表输出学生的学号、各科成绩和平均成绩
//
//2、定义静态成员,用于记录对象的个数,每创建一个对象,对象个数增1,每撤销一个对象,对象个数减1,输出对象的个数
//
//输入
//先输入n值
//
//再输入n组学生信息
//
//输出
//先输出n组学生信息
//
//再输出静态成员的值
//
//样例输入
//3
//10001 90 80 70
//10002 80 70 60
//10003 70 60 50
//样例输出
//10001 90 80 70 80
//10002 80 70 60 70
//10003 70 60 50 60
//3
#include<iostream>
using namespace std;
class score
{
string num;
double math;
double english;
double programming;
double avscore;
public:
static int counter;
score()
{
counter++;
}
~score()
{
counter--;
}
void inscore();
void showscore();
};
int score::counter = 0;
void score::inscore()
{
cin >> num >> math >> english >> programming;
avscore = (math + english + programming) / 3.0;

}
void score::showscore()
{
cout << num << " ";
cout << math << " " << english << " " << programming << " " << avscore << endl;
}
int main()
{
int n, i, j, k;
cin >> n;
score* s;
s = new score[n];//这一块儿要好好学习一下,基于要求所说score类的对象个数不确定
for (i = 0; i < n; i++)
{
s[i].inscore();
}
for (i = 0; i < n; i++)
{
s[i].showscore();
}
cout << score::counter;
return 0;
}

———————————————————————
析构函数是用于取消对象的成员函数
当一个对象作用域结束时,作用域包含很多种,有函数作用域,有文件作用域等等。(下方代码为证!!!)
当作用域结束时,系统自动调用析构函数(如果用指针创建一个新的对象,必须要在代码里写上delete p)

 #include <iostream>
using namespace std; 
class Point {

public:
int X, Y;
static int Countp;
Point(int x = 0, int y = 0)
{
X = x; Y = y; Countp++;
cout << "b" << endl;
}
Point(const Point& p)
{
X = p.X; Y = p.Y; Countp++;
cout << "a" << endl;
}
~Point() { Countp--; cout << "c" << endl; }
};
Point myfun(Point pp0, Point pp1, Point pp2)
{
Point p;
p.X = pp1.X + pp2.X;
p.Y = pp1.Y + pp2.Y;
return p;
}
int Point::Countp = 0;
int main()
{
int a, b, c;
cin >> a >> b >> c;
Point pp0, pp1(a, b), pp2(c); 
Point p = myfun(pp0, pp1, pp2);
cout << p.X << "," << p.Y << "," << Point::Countp;
cout << endl;
return 0;
}

在这里插入图片描述
补充一下友元的引用有什么用:
在这里插入图片描述

特殊情况:
例如:
1. 有指针的(只创建单个对象)
int main()
{ Date *pd = new ( Date );
   pd->SetDate(2001,10,1) ;     pd->PrintDate() ;
   delete pd ;
} 
delete之后,系统才后自动调用
2. 有指针的(创建类对象数组)
int main()
{
Samp *p;
p=new Samp[5;
……
delete[]p;//
return 0;
}
3.还有一种
student::student(int i, char* c, int a, float s)
{
cout << "constructing…" << endl;
id = i;
age = a;
score = s;
name = new char[strlen(c) + 1];//这儿申请了一个数组,动态申请建议回顾
if (name != 0)
strcpy(name, c);
}
student::~student()
{
cout << "destructing…" << endl;
delete [] name;//非常必要
}

析构函数的作用是进行清除对象,释放内存等
没有用户定义析构函数时,系统提供缺省版本的析构函数
析构函数名为: ~ 类名
析构函数没有参数,也没有返回类型

带参数的构造函数

带参数的构造函数在建立对象时,以特定的数据初始化对象的数据成员。

函数块里初始化:
Date:: Date(int y, int m, int d)// 带参数构造函数
 { year = y ;    month = m ;    day = d ;
   cout <<year <<"/"<<month<<"/"<<day<<": Date object initialized."<<"\n" ;
 }
 初始化式初始化:
 Date:: Date(int y, int m, int d) : year(y), month(m), day(d)
{ cout<<year<<"/"<<month<<"/"<<day<<": Date object initialized."<<"\n" ;
} 
(“初始式”可以调用类类型成员或基类构造函数)
(初始化式的顺序在后面的继承里会提到)

重载构造函数

类 X 具有一个或多个构造函数,创建类 X 的对象时,根据参数的类型和个数进行匹配。

class  X
{ public:
       X ( ) ;
       X( int ) ;
       X ( int,  char ) ;
       X ( double,  char ) ;...
} ;
void f ( )
  { X  a ;
     X  b ( 1 ) ;
     X  c ( 1 ,  'c' ) ;
     X  d ( 2.3 ,  'd' ) ;...
  }

注意:

构造函数可以使用缺省参数,但谨防二义性(默认参数值搞的事儿)
例:
class  X
{ public:
    X ( ) ;
    X( int  i = 0 ) ;...
} ;
int main ()
{ X  one(10) ;        // 正确
X two;   //二义性,错误...
}

复制构造函数

复制构造函数用一个已有同类对象的数据对正在建立的对象
进行数据初始化
C++为类提供默认版本的复制构造函数
程序员可以定义用户版本的复制构造函数
语法形式
类名 :: 类名(const 类名 & 引用名 , …);
(const是为了保护实参不被改变,让它只读)

class  A 
{ public : 
       A ( int ) ; 
       A (const A &);
       A ( const A & ,  int =1 ) ;} ;
  …
A  a ( 1 ) ;
A  b ( a );//调用复制构造函数
A  b ( a , 0 ) ; //调用复制构造函数
A  c = b ;//调用复制构造函数

补充一下值传参的特性:

#include<iostream>
using namespace std;
class A
{
public:
A() { a = 5; }
void printa()
{
cout << "A::a=" << a << endl;
}
private:
int a;
friend class B;
};
class B
{
public:
void display1(A t)
{
t.a++;
cout << "display:a=" << t.a << endl;
}
void diaplat2(A t)
{
t.a--;
cout << "diaplay2:a=" << t.a << endl;
}
};
int main()
{
A obj1;
B obj2;
obj1.printa();
obj2.display1(obj1);
obj1.printa();
return 0;
}

在这里插入图片描述
在这里插入图片描述

调用复制构造函数的时机

三种

1.用已有对象初始化新创建对象 
(这个B肯定要析构,明面上的事情)
Location ( const Location  & p ) 
 { X = p.X ;  Y = p.Y ;   
 cout << "Copy_constructor called." << endl ; }
 ……
int main()
{ Location  A ( 1, 2 ) ;
   Location B ( A ) ;
   cout << "B : " << B.GetX() << " , " << B.GetY() << endl ;
} 
2.函数的类类型实参初始化形参时,要调用复制构造函数 
(不要忘记析构形参对象!!!转下图)
 Location( const Location & p )     //复制构造函数
       { X = p.X ;  Y = p.Y ;   cout << "Copy_constructor called." << endl ;  }
void f ( Location  p )//
 { cout << "Funtion:" << p.GetX() << "," << p.GetY() << endl ; }
int main()
   {  Location A ( 1, 2 ) ; 
       f ( A ) ;
  } 
3.函数返回类型为类类型时,通过复制构造函数建立临时对象
(不要忘记匿名对象也需要析构!!!转下图)
 Location ( int xx = 0, int yy = 0 ) 
 { X = xx ; Y = yy ;
   cout << "Object constructed." << endl ;
 }
Location g()
{ Location A ( 1, 2 ) ;
       return A ;
}
int main() 
{ Location  B ;
  B = g() ;
}

在这里插入图片描述
在这里插入图片描述

浅复制和深复制

浅复制,调用系统默认的复制构造函数。
在这里插入图片描述
析构完一个之后,它所指向的内存空间也就已经消失了,也就是绿色的那条。但是紧接着在析构另一个的时候,会出现错误,显示内存已经释放。
深复制,调用用户自定义的复制构造函数。
在这里插入图片描述


原文地址:https://blog.csdn.net/weixin_50512050/article/details/117772264

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!