文章目录
一.定义类和对象
1.概念
1.面向对象编程的程序基本单位是类
2.类是数据和操作数据的函数的封装
3.类的对象使用自己的方法完成对数据的操作
4.类可以隐藏数据和操作细节,对象通过类接口与外部通信
2.类的相关概念
- class 缺省说明时,其成员被认为是私有的
- struct 若不特别指出,其所有成员都是公有的
- union 其所有成员都是公有的,且不能更改
3.类由成员构成
1.数据成员–描述对象的属性
2.成员函数–描述对象的方法
例子
#include<iostream>
using namespace std ;
class Tdate
{ public:
void Set(int m, int d, int y ) { month = m ; day = d ; year = y ; }
int IsLeapYear()
{ return ( year%4 == 0 && year%100 != 0 )||( year%400 == 0); }
void Print() { cout << year << "." << month << "." << day << endl ; }
private:
int month; int day; int year;
};
int main()
{ Tdate a ;
a.Set (10, 16, 2003) ; a.Print() ;
}
说明:该例子的成员函数使用内联函数处理
#include<iostream>
using namespace std ;
class Tdate
{ public:
void Set(int m, int d, int y ) ;
int IsLeapYear();
void Print() ;
private:
int month; int day; int year;
};
void Tdate :: Set(int m, int d, int y )
{ month = m ; day = d ; year = y ; }
int Tdate :: IsLeapYear()
{ return ( year%4 == 0 && year%100 != 0 )||( year%400 == 0); }
void Tdate :: Print() { cout <<year<<"."<<month<<"."<<day<<endl ; }
int main()
{ Tdate a ;
a.Set (10, 16, 2003) ; a.Print() ;
}
说明:该例子的成员函数使用外联函数处理
4.成员属性
1.public 公有 公有段的成员是提供给外部的接口
2.protected 保护 保护段成员在该类和它的派生类中可见
3.private 私有 私有段成员仅在类中可见
5.注意事项
1. class link { link * next ; …… };
允许已定义类名出现在类的说明中
2.类可以无名,用于直接声明对象class { …… } mydate ;
直接声明一个对象
3.类是一个程序包。可以只有数据成员或只有成员函数,或者为空。
#include<iostream>
using namespace std ;
class empty { } ;
int main()
{ empty e1 ;
cout<<" &e1 = " << &e1 << endl ;
cout<<" sizeof e1 = " << sizeof(e1) << endl ;
}
空类作用:
①作为标记类:空类可以用作标记类,用于表示某种特定的类型或状态。通过空类的类型信息,可以在编译时进行类型检查或在运行时进行特定的操作。
②作为基类:空类可以作为其他类的基类,派生类可以继承空类的特性,例如虚函数、虚继承等。这种情况下,空类可能会在多态性和继承方面发挥作用。
③占位符:有时候在设计初期,可能会定义一些空类作为占位符,以便后续开发时填充具体的成员变量和函数。
二.构造函数和析构函数
1.构造函数
1.定义:用于创建对象的特殊成员函数,当创建对象时,系统自动调用构造函数
2.作用:①为对象分配空间;②对数据成员赋初值;③请求其他资源
2.析构函数
1.定义:用于取消对象的成员函数,当一个对象作用域结束时,系统自动调用析构函数
2.作用:清除对象,释放内存
3.带参数的构造函数
第一种:
class Date
{ public:
Date(int,int,int) ; ~Date() ;
void SetDate( int y, int m, int d ) ;
void IsLeapYear() ; void PrintDate() ;
private: int year, month, day ;
} ;
Date:: Date(int y, int m, int d)
{ year = y ; month = m ; day = d ;}
第二种(使用“初始式”的构造函数形式为:)
class Date
{ public:
Date(int,int,int) ; ~Date() ;
void SetDate( int y, int m, int d ) ;
void IsLeapYear() ; void PrintDate() ;
private: int year, month, day ;
} ;
Date:: Date(int y, int m, int d) : year(y), month(m), day(d)
注:默认参数再赋值会替代默认参数。
4.类类型数据成员
#include<iostream>
using namespace std ;
class A
{ public :
A ( int x ) : a ( x ) { }
int a ;
} ;
class B
{ public :
B( int x, int y ) : aa( x ), b( y ) { }
void out()
{ cout << "aa = " << aa.a << endl << "b = " << b << endl ; }
private :
int b ;
A aa ;
} ;
解释: B( int x, int y ) : aa( x ), b( y ) { }中的aa(x)为A类中的构造函数。
5.重载构造函数
避免二义性:可以使用缺省参数
lass X
{ public:
X ( ) ;
X( int i = 0 ) ;
…...
} ;
int main ()
{ X one(10) ; // 正确
x tow; //不知道调用哪个函数
}
6.复制构造函数
①定义
用一个已有同类对象的数据对正在建立的对象,进行数据初始化
②语法
类名 :: 类名(const 类名 & 引用名 , …);
③调用时机:
1.用已有对象初始化新创建对象
#include<iostream>
using namespace std ;
class Location
{ public :
Location ( int xx = 0, int yy = 0 ) { X = xx ; Y = yy ; }
Location ( const Location & p )
{ X = p.X ; Y = p.Y ; cout << "Copy_constructor called." << endl ; }
int GetX() { return X ; }
int GetY() { return Y ; }
private : int X , Y ;
} ;
int main()
{ Location A ( 1, 2 ) ;
Location B ( A ) ;
cout << "B : " << B.GetX() << " , " << B.GetY() << endl ;
}
2.函数的类类型实参初始化形参时,要调用复制构造函数
#include<iostream>
using namespace std ;
class Location
{ public:
Location( int xx = 0 , int yy = 0 )
{ X = xx ; Y = yy ; cout << "Constructor Object.\n" ; }
Location( const Location & p ) //复制构造函数
{ X = p.X ; Y = p.Y ; cout << "Copy_constructor called." << endl ; }
~Location() { cout << X << "," << Y << " Object destroyed." << endl ; }
int GetX () { return X ; } int GetY () { return Y ; }
private : int X , Y ;
} ;
void f ( Location p ) { cout << "Funtion:" << p.GetX() << "," << p.GetY() << endl ; }
int main()
{ Location A ( 1, 2 ) ;
f ( A ) ;
}
3.函数返回类类型时,通过复制构造函数建立临时对象
#include<iostream>
using namespace std;
class Location
{
public:
Location(int xx = 0, int yy = 0)
{
X = xx; Y = yy;
cout << "Object constructed." << endl;
}
Location(const Location& p)
{
X = p.X; Y = p.Y;
cout << "Copy_constructor called." << endl;
}
~Location()
{
cout << X << "," << Y << " Object destroyed." << endl;
}
int GetX() { return X; }
int GetY() { return Y; }
private: int X, Y;
};
Location g()
{
Location A(2, 1);
return A;
}
int main()
{
g();
}
④浅复制和深复制
前者对栈里面的资源进行拷贝
后者对栈和堆都进行拷贝
原因:
①默认复制构造函数可以完成对象的数据成员值简单的复制,对象的数据资源是由指针指示的堆时
②默认复制构造函数,仅作指针值复制
#include<iostream>
using namespace std;
class A
{
public:
A(int a)
{
x = new int;
*x = a;
}
~A()
{
delete x;
}
int* x;
};
int main()
{
A a(10);
A b = a;
cout << a.x << endl;
cout << b.x << endl;
cout << *(a.x) << endl;
cout << *(b.x) << endl;
}
当我们使用复制构造函数的时候,我们会将xdelete两次,导致空指针的释放,而出现错误。
如果我们进行深拷贝:
A(const A& p)
{
x = new int;
*x = *(p.x);
}
那么在复制的时候也会重建一个地址
⑤移动构造函数
在C++中,移动构造函数是一种特殊的构造函数,用于实现对象的移动语义。移动构造函数通常用于将一个对象的资源所有权从一个对象转移给另一个对象,而不是进行深层复制。这在处理动态分配的资源(如内存、文件句柄等)时非常有用,可以提高程序的性能和效率。
#include <iostream>
class MyClass {
private:
int* array;
int size;
public:
// 构造函数
MyClass(int s) : size(s), array(new int[s]) {
std::cout << "普通构造函数,数组大小为 " << size << std::endl;
}
// 移动构造函数
MyClass(MyClass&& other) noexcept : size(other.size), array(other.array) {
other.size = 0;
other.array = nullptr;
std::cout << "移动构造函数,数组大小为 " << size << std::endl;
}
// 析构函数
~MyClass() {
if (array != nullptr) {
delete[] array;
}
}
};
int main() {
MyClass obj1(5); // 调用普通构造函数
MyClass obj2(std::move(obj1)); // 调用移动构造函数
return 0;
}
三.常成员
常数据成员是指数据成员在实例化被初始化后约束为只读。
常成员函数是指成员函数的this指针被约束为指向常量的常指针,函数体内不能修改数据成员的值。
1.常数据成员
include<iostream>
using namespace std;
class Mclass
{ public :
int k;
const int M; //说明常数据成员
Mclass() : M(5) { } //用初始式对常数据成员赋值//初始式优先级最高
void testFun()
{ //M++; //错误,不能在成员函数中修改常数据成员
k++; //可以修改一般数据成员
}
} ;
对象拥有自己的常数据成员地址不同
2.常对象
#include<iostream>
using namespace std;
class T_class
{ public:
int a, b; //公有数据成员
T_class( int i, int j ) { a=i; b=j; }
} ;
int main()
{ const T_class t1( 1, 2 ); //t1是常对象
T_class t2( 3, 4 );
//t1.a=5; //错误,不能修改常对象的数据成员
//t1.b=6; //错误,不能修改常对象的数据成员
t2.a=7;
t2.b=8;
cout<<"t1.a="<<t1.a<<'\t'<<"t1.b="<<t1.b<<endl;
cout<<"t2.a="<<t2.a<<'\t'<<"t2.b="<<t2.b<<endl;
}
3.常成员函数
#include<iostream>
using namespace std ;
class Simple
{ int x, y ;
public :
void setXY ( int a, int b) { x = a ; y = b ; }
void printXY()const //void constFun ( const Simple * const this )
//{ cout << x << "," << y << endl ; } ;错误
4.静态成员
1.类成员冠以static声明时,称为静态成员。
2.静态数据成员为同类对象共享。
3.静态成员函数与静态数据成员协同操作。
①静态数据成员
class X { char ch ; static int s ; …… };
int X :: s = 0 ;
X a , b , c , d ;
两种调用
#include<iostream>
using namespace std ;
class counter
{ static int num ;//声明静态数据成员
public :
void setnum ( int i ) { num = i ; } //成员函数访问 静态数据成员
void shownum() { cout << num << '\t' ; }
} ;
int counter :: num = 0 ; //定义静态数据成员
int main ()
{ counter a , b ;
a.shownum() ; b.shownum() ;
a.setnum(10) ;
a.shownum() ; b.shownum() ;//调用成员函数访问 私有静态数据成员
cout<<endl ;
cout<<counter:: num<<endl;
}
②静态成员函数
静态成员函数数冠以关键字static
静态成员函数没有this指针,只能对静态数据操作
在类外调用静态成员函数用 “类名 :: ”作限定词,或通过对象调用(与数据相同)
class X
{ public :
static void StaFun ( int i , X *ptr ) ;
int staDat ;
} ;
void X :: StaFun ( int i , X * ptr )
{
staDat=i// 错误 静态成员函数没有this指针,只能对静态数据操作
ptr -> staDat = i ; // 正确
}
void g()
{ X obj ;
X :: StaFun ( 1, & obj ) ; // 正确
obj.StaFun ( 1, & obj ) ; // 正确
}
四.友元
注意:
①友元是对类操作的辅助手段。友元能够引用类中本来被隐蔽的信息
②使用友元目的是基于程序的运行效率
③运算符重载的某些场合需要使用友元,友元可以是函数,也可以是类
④友元关系是非传递的。即 Y 是 X 的友元,Z 是 Y 的友元,但 Z 不一定是X的友元
1.友元函数
#include<iostream>
using namespace std ;
#include<math.h>
class Point
{ public:
Point(double xi, double yi) { X = xi ; Y = yi ;}
double GetX() { return X ; }
double GetY() { return Y ; }
friend double Distance ( Point & a, Point & b ) ;//友元声明友元位置没有关系
private: double X, Y ;
} ;
double Distance(Point & a, Point & b )
{ double dx = a.X - b.X ;
double dy = a.Y - b.Y ;
return sqrt ( dx * dx + dy * dy ) ;
}
int main()
{ Point p1( 3.0, 5.0 ) , p2( 4.0, 6.0 ) ;
double d = Distance ( p1, p2 ) ;
cout << "This distance is " << d << endl ;
}
2.友元类
若F类是A类的友元类,则F类的所有成员函数都是A类的友元函数
友元类通常设计为一种对数据操作或类之间传递消息的辅助类
#include<iostream>
using namespace std ;
class A
{ friend class B ;//友元类
public :
void Display() { cout << x << endl ; } ;
private :
int x ;
} ;
class B
{ public :
void Set ( int i ) { Aobject . x = i ; }
void Display () { Aobject . Display () ; }
private :
A Aobject ;
} ;
int main()
{ B Bobject ;
Bobject . Set ( 100 ) ;
Bobject . Display () ;
}
五.类的包含
->>类的包含(称为has A)是程序设计中一种软件重用技术。即定义一个新的类时,通过编译器把另一个类 “抄”进来。
->> 当一个类中含有已经定义的类类型成员,带参数的构造函数对数据成员初始化,须使用初始化语法形式。
->>构造函数 ( 变元表 ) : 对象成员1( 变元表 ) , … , 对象成员n ( 变元表 ) ;
#include<iostream>
#include<cmath>
using namespace std;
class point
{
public:
point(int a, int b) :x(a), y(b){}
int Getx() { return x; }
int Gety() { return y; }
private:
int x;
int y;
};
double Getdis(point p1, point p2)
{
double x = double(p1.Getx() - p2.Getx());
double y = double(p1.Gety() - p2.Gety());
return sqrt(x * x + y * y);
}
class Distance
{
public:
Distance(point a,point b):x(a),y(b){}
void Calculate()
{
cout << "距离:" << Getdis(x, y);
}
private:
point x;
point y;
};
int main()
{
point p1(20, 10);
point p2(10, 20);
Distance Dis(p1, p2);
Dis.Calculate();
}