类和对象
定义一个类
class className
{
private:
私有属性和函数
public:
公有属性和函数
protected:
保护属性和函数
};
- 如果某个成员前面没有上述关键字,则缺省地被认为
是私有成员
class Man
{
int nAge; // 私有成员
char szName[20]; // 私有成员
public:
void SetName(char * szName)
{
strcpy( Man::szName,szName);
}
};
类成员可被访问的范围
– private (私有成员):
- 基类的成员函数
- 基类的友元函数
– public (公有成员):
- 可以在任何地方访问
– protected( 保护成员):
- 基类的成员函数
- 基类的友元函数
- 派生类的成员函数可以访问当前对象的基类的保护成员
在类的成员函数内部,能够访问:
– 当前对象的全部属性、函数;
– 同类其它对象的全部属性、函数。
在类的成员函数以外的地方,只能够访问该类对象的
公有成员
成员函数的重载及参数缺省
- 成员函数也可以重载
- 成员函数可以带缺省参数
- 使用缺省参数要注意避免有函数重载时的二义性
#include <iostream>
using namespace std;
class Location
{
private :
int x, y;
public:
void init( int x=0 , int y = 0 );
void valueX( int val )
{
x = val ;
}
int valueX()
{
return x;
}
};
void Location::init( int X, int Y)
{
x = X;
y = Y;
}
int main()
{
Location A,B;
A.init(5);
A.valueX(5);
cout << A.valueX();
return 0;
}
输出:
5
构造函数
基本概念
- 成员函数的一种
- 名字与类名相同,可以有参数,不能有返回值(void也不行)
- 作用是对对象进行初始化,如给成员变量赋初值
class Complex
{
private :
double real, imag;
public:
Complex( double r, double i = 0);
};
Complex::Complex( double r, double i)
{
real = r;
imag = i;
}
Complex c1; // error, 缺少构造函数的参数
Complex * pc = new Complex; // error, 没有参数
Complex c1(2); // OK
Complex c1(2,4), c2(3,5);
Complex * pc = new Complex(3,4);
- 如果定义类时没写构造函数,则编译器生成一个默认的无参数的构造函数
- 默认构造函数无参数,不做任何操作
- 如果定义了构造函数,则编译器不生成默认的无参数的构造函数
class Complex
{
private :
double real, imag;
public:
void Set( double r, double i);
}; //编译器自动生成默认构造函数
Complex c1; //默认构造函数被调用
Complex * pc = new Complex; //默认构造函数被调用
- 对象生成时构造函数自动被调用。对象一旦生成,就再也不能在其上执行构造函数
- 一个类可以有多个构造函数,参数个数或类型不同
class Complex
{
private :
double real, imag;
public:
void Set( double r, double i );
Complex(double r, double i );
Complex (double r );
Complex (Complex c1, Complex c2);
};
- 构造函数最好是public的,private构造函数不能直接用来初始化对象
构造函数在数组中的使用
class Test
{
public:
Test( int n) { } //(1)
Test( int n, int m) { } //(2)
Test() { } //(3)
};
Test array1[3] = { 1, Test(1,2) };
- 三个元素分别用(1),(2),(3)初始化
Test array2[3] = { Test(2,3), Test(1,2) , 1};
- 三个元素分别用(2),(2),(1)初始化
Test * pArray[3] = { new Test(4), new Test(1,2) };
- 两个元素分别用(1),(2) 初始化
复制构造函数
基本概念
- 只有一个参数,即对同类对象的引用。
- 形如 X::X( X& )或X::X(const X &), 二者选一后者能以常量对象作为参数
- 如果没有定义复制构造函数,那么编译器生成默认复制构造函数。默认的复制构造函数完成复制功能。
- 如果定义的自己的复制构造函数,则默认的复制构造函数不存在。
- 不允许有形如 X::X( X )的构造函数。
例:
class Complex
{
public :
double real,imag;
Complex(){ }
Complex( const Complex & c )
{
real = c.real;
imag = c.imag;
cout << “Copy Constructor called”;
}
};
Complex c1;
Complex c2(c1);//调用自己定义的复制构造函数,输出 Copy Constructor called
复制构造函数起作用的三种情况
1、当用一个对象去初始化同类的另一个对象时。
Complex c2(c1);
Complex c2 = c1; //初始化语句,非赋值语句
- 注意:对象间赋值并不导致复制构造函数被调用
CMyclass c1,c2;
c1.n = 5;
c2 = c1;
没有调用复制构造函数
2、如果某函数有一个参数是类 A 的对象,那么该函数被调用时,类A的复制构造函数将被调用。
class A
{
public:
A() { };
A( A & a)
{
cout << "Copy constructor called" <<endl;
}
};
void Func(A a1){ } //函数的参数是类 A 的对象
int main()
{
A a2;
Func(a2); //A的复制构造函数被调用
return 0;
}
程序输出结果为: Copy constructor called
3、如果函数的返回值是类A的对象时,则函数返回时,A的复制构造函数被调用
class A
{
public:
int v;
A(int n) { v = n; };
A( const A & a) {
v = a.v;
cout << "Copy constructor called" <<endl;
}
};
A Func() //函数的返回值是类A的对象
{
A b(4);
return b;
}
int main()
{
cout << Func().v << endl; //函数返回时,A的复制构造函数被调用
return 0;
}
输出结果:
Copy constructor called
4
类型转换构造函数
- 定义转换构造函数的目的是实现类型的自动转换。
- 只有一个参数,而且不是复制构造函数的构造函数,一般就可以看作是转换构造函数。
- 当需要的时候,编译系统会自动调用转换构造函数,建立一个无名的临时对象(或临时变量)。
例:
- 隐式类型转换构造函数
class Complex
{
public:
double real, imag;
Complex( int i) // 类型转换构造函数
{
cout << "IntConstructor called" << endl;
real = i; imag = 0;
}
Complex(double r,double i) {real = r; imag = i; }
};
int main ()
{
Complex c1(7,8);
Complex c2 = 12; //调用类型转换构造函数
c1 = 9; // 9 被自动转换成一个临时Complex 对象
cout << c1.real << "," << c1.imag << endl;
return 0;
}
- 显式类型转换构造函数,必须显式调用
class Complex
{
public:
double real, imag;
explicit Complex( int i) //显式类型转换构造函数
{
cout << "IntConstructor called" << endl;
real = i; imag = 0;
}
Complex(double r,double i) {real = r; imag = i; }
};
int main ()
{
Complex c1(7,8);
Complex c2 = Complex(12);
c1 = 9; // error, 9不能被自动转换成一个临时Complex对象
c1 = Complex(9) //ok
cout << c1.real << "," << c1.imag << endl;
return 0;
}
析构函数
基本概念
- 名字与类名相同,在前面加‘~’, 没有参数和返回值,一个类最多只能有一个析构函数。
- 析构函数对象消亡时即自动被调用。可以定义析构函数来在对象消亡前做善后工作,比如释放分配的空间等。
- 如果定义类时没写析构函数,则编译器生成缺省析构函数。缺省析构函数什么也不做。
- 如果定义了析构函数,则编译器不生成缺省析构函数。
调用情况
1、对象数组生命期结束时,对象数组的每个元素的析构函数都会被调用
class Ctest
{
public:
~Ctest() { cout<< "destructor called" << endl; }
};
int main ()
{
Ctest array[2];
cout << "End Main" << endl;
return 0;
}
输出:
End Main
destructor called
destructor called
2、delete 运算导致析构函数调用。
Ctest * pTest;
pTest = new Ctest; // 构造函数调用
delete pTest; // 析构函数调用
---------------------------------------------------------
pTest = new Ctest[3]; // 构造函数调用3次 次
delete [] pTest; // 析构函数调用3次
3、参数对象消亡也会导致析构函数被调用
4、函数调用的返回值(临时对象)被用过后,该临时对象析构函数被调用
class CMyclass
{
public:
~CMyclass() { cout << "destructor" << endl; }
};
CMyclass obj;
CMyclass fun(CMyclass sobj ) // 参数对象消亡会导致析构函数被调用
{
return sobj; // 函数调用返回时生成临时对象返回
}
int main()
{
obj = fun(obj); // 函数调用的返回值(临时对象)被用过后,该临时对象析构函数被调用
return 0;
}
输出:
destructor
destructor
destructor
例:
class Demo
{
int id;
public:
Demo(int i)
{
id = i;
cout << "id=" << id << " constructed" << endl;
}
~Demo()
{
cout << "id=" << id << " destructed" << endl;
}
};
Demo d1(1); //(1)构造函数被调用
void Func()
{
static Demo d2(2); //生成静态局部对象(8)构造函数被调用
Demo d3(3); //(9)构造函数被调用
cout << "func" << endl; //(10)输出“func”
} //函数调用结束后引发析构函数调用,但静态局部对象程序结束时才会消亡(11)非静态局部对象d3消亡
int main ()
{
Demo d4(4); //(2)构造函数被调用
d4 = 6; //(3)6被转换成一个临时对象 (4)临时对象执行完语句后消亡,引发析构函数调用
cout << "main" << endl; //(5)输出main
{
Demo d5(5); //生成一个局部对象(6)构造函数被调用
} //局部对象的生存周期到大括号结束,(7)析构函数被调用
Func(); //调用函数
cout << "main ends" << endl; //(12)输出“main ends”
return 0;
} //程序结束(13)d4消亡(一般来说先构造的后消亡)(14)静态局部对象d2消亡(15)d1消亡
输出结果: :
id=1 constructed
id=4 constructed
id=6 constructed
id=6 destructed
main
id=5 constructed
id=5 destructed
id=2 constructed
id=3 constructed
func
id=3 destructed
main ends
id=6 destructed
id=2 destructed
id=1 destructed