C ++ 类
@(C/C++)[c++, 类]
1 类的定义/构造
1.1 关于类的理解
★关于类的理解
\
类,也就是带有函数的结构体,即,既有”名词”(成员变量),也有”动词”(成员函数).
1.2 类的定义
类的定义
注意,这种定义方式,
成员函数
写在了类的里面。还有一种方式是将函数原型
写在里面,
函数定义
写在外面的。见后面
所谓类
,不过是以类
形式自定义的新的特殊的类型
。就像int
double
char
。说它特殊,是因为,还带有函数。
用类
定义变量的过程称为实例化,所定义的变量称为对象。
对象的内存空间
◆对象的大小 =
所有成员变量的大小之和(上述例子例子中是两个整型之和)
◆对于上述例子中的CRectangle
类的对象, sizeof(CRectangle) = 8
每个对象各有自己的存储空间
◆一个对象的某个成员变量被改变, 不会影响到其他的对象
类的第二种定义方式
注意,
成员函数
仅仅在内部声明,而在外部定义
2 类成员的可访问范围
关键字 – 类成员可被访问的范围
•private
:指定私有成员, 只能在成员函数内
被访问, 注意此为缺省设定
,也就是,若没指定访问范围,就是默认private
•public
:指定公有成员, 可以在任何地方
被访问
•protected
:指定保护成员
三种关键字出现的次数和先后次序都没有限制
class className {
private:
私有属性和函数
public:
公有属性和函数
protected:
保护属性和函数
};
3 内联成员函数/重载
和正常的成员函数以及重载是一样的.
使用缺省参数要注意避免有函数重载时的二义性
class Location {
private:
int x, y;
public:
void init( int x =0, int y = 0 );
void valueX( int val = 0 ) { x = val; }
int valueX() { return x; }
};
Location A;
A.valueX(); //错误, 编译器无法判断调用哪个
3 构造函数
定义
成员函数的一种
★ 名字与类名相同,可以有参数,不能有返回值(void也不行)
★ 作用是对对象进行初始化,如给成员变量赋初值
★ 如果定义类时没写构造函数,则编译器生成一个默认的无参数的构造函数
☆默认构造函数无参数,不做任何操作
意义
1)
构造函数执行必要的初始化工作,有了构造函数,就不必专门再写初始化函数,也不用担心忘记调用初始化函数。
2)
有时对象没被初始化就使用,会导致程序出错。因而引进构造函数
4 复制构造函数
4.1 复制构造函数的定义
★ 只有一个参数,即对同类对象的引用
。
★ 形如 X::X( X& )或X::X(const X &), 二者选一, 后者能以常量对象作为参数
★ 如果没有定义复制构造函数,那么编译器生成默认复制构造函数。默认的复制构造函数完成复制功能 i.e换句话说, 自己编写的复制构造函数不一定具有复制功能
的!
4.2 复制构造函数的作用
1)
当用一个对象去初始化同类的另一个对象时。
Complex c2(c1);
Complex c2 = c1; //初始化语句,非赋值语句
上面两个语句等价
2)
如果某函数有一个参数是类 A 的对象, 那么该函数被调用时,类A的复制构造函数将被调用。
class A
{
public:
A() { };
A( A & a){
cout << "Copy constructor called" <<endl;
}
};
void Func(A a1){ }
//实参把值传递过去时(i.e 赋值/初始化形参),用的是复制构造函数
int main(){
A a2;
Func(a2);
return 0;
}
注意
, 形参的值是由复制构造函数给予的,而复制构造函数是自己编写的
, 所以形参的值并不一定等于实参了
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 b(4);
return b;
}
int main() {
cout << Func().v << endl;
%Func()会产生一个返回的临时对象,这个对象的值是由复制构造函数给予的,其中复制构造函数的参数是 b .
return 0;
}
注意 Func()会产生一个返回的
临时对象
.
输出结果:
Copy constructor called ,因为调用了复制构造函数
4 因为用了cout
5 类型转换构造函数
目的
•实现类型的自动转换 特点
特点
•只有一个参数,(但是不能是自身类的引用或者常引用,因为那就成了复制构造函数)
•不是复制构造函数
编译系统会自动调用转换构造函数, 建立一个 临时对象 / 临时变量
;
6 析构函数
6.1 析构函数的构造
• 成员函数的一种
• 名字与类名
相同
• 在前面加 ‘~’
• 没有参数和返回值
• 一个类最多只有一个
析构函数
6.2 析构函数的意义
对象消亡时 自动被调用
, 在对象消亡前做善后工作释放分配的空间等 (若不及时释放消亡的对象占用的空间,则会浪费大量空间,造成内存泄漏.)
6.3 析构函数的特点
★ 定义类时没写析构函数, 则编译器生成缺省析构函数
★ 不涉及释放用户申请的内存释放等清理工作(也就是如果用了new申请内存, 一定要自己编个析构函数,用delete释放掉)
★ 定义了析构函数, 则编译器不生成缺省析构函数
class String{
private :
char * p;
public:
String () {
p = new char[10];
}
~ String ();
};
String ::~ String() {
delete [] p;
}
6.4 析构函数和数组
对象数组生命期结束时
对象数组的每个元素的析构函数都会被调用(很显然的事)
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
析构函数内容输出了两次, 因为对于对象数组中每个对象都使用了析构函数.
6.5 析构函数和运算符 delete
delete 运算导致析构函数调用
1)
Ctest * pTest;
pTest = new Ctest; //构造函数调用
delete pTest; //析构函数调用
2)
pTest = new Ctest[3]; //构造函数调用3次
delete [] pTest; //析构函数调用3次
以下代码是用来探知,析构函数和构造函数的机制
class Demo {
int id;
public:
Demo( int i ){
id = i;
cout << “id=” << id << “ Constructed” << endl;
}
~Demo(){
cout << “id=” << id << “ Destructed” << endl;
}
};
Demo d1(1);
void Func()
{
static Demo d2(2);
Demo d3(3);
cout << “Func” << endl;
}
int main ()
{
Demo d4(4);
d4 = 6;
cout << “main” << endl;
{ Demo d5(5); }
Func();
cout << “main ends” << endl;
return 0;
}
输出:
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
//c++机制,最后一起析构时,后构造的先析构
注意,在不同的编译器中,构造函数和析构函数的表现可能不同.
7 静态成员/函数
7.1 静态成员的构建
★静态成员
的构建:
在说明前面加了static
关键字的成员。
class CRectangle
{
private:
int w, h;
static int nTotalArea; //静态成员变量
static int nTotalNumber;
public:
CRectangle(int w_,int h_);
~CRectangle();
static void PrintTotal();
//静态成员函数
};
7.2 静态成员的特点
★静态成员--变量--的特点:
普通成员变量每个对象有各自的一份,
而静态成员变量所有的此类对象一共就一份,为所有对象共享。
注意,sizeof
运算符不会计算静态成员变量。
class CMyclass
{
int n;
static int s;
};
则 sizeof
( CMyclass ) = 4, Why?
因为
,静态成员变量并不是放在所定义的对象的内部,而是放在某个特定的内存空间,为所有此类对象所共享.
★静态成员--函数--的特点:
普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用与某个对象。
综上
, 静态成员不需要通过对象就能访问 ,这点对于普通成员是不可能的.(当然,也可以通过特定对象进行访问,但是并不意味着作用于特定对象) 因此,静态成员的访问可以有一下几种形式
1) 类名::成员名
CRectangle::PrintTotal();
这种方式对于普通成员函数进行访问是不可能的
2) 对象名.成员名
CRectangle r; r.PrintTotal();
虽然通过对象进行访问,但是并不意味着此函数作用于此对象, 因为此函数是静态成员函数,
3) 指针->成员名
CRectangle * p = &r; p->PrintTotal();
同理, 不意味着作用于某一特定对象.
4) 引用.成员名
CRectangle & ref = r; int n = ref.nTotalNumber
同理, 不意味着作用于特定某一对象.
★
在静态成员函数中,不能访问非静态成员变量, 也不能调用非静态成员函数。
因为.静态成员函数并不具体作用与某一特定对象,若包含非静态成员变量,则修改的此变量属于哪一个对象???,对此无法解释的通.
8 成员对象&封闭类
成员对象: 一个类的成员变量是另一个类的对象包含成员对象 的类叫 封闭类 (Enclosing)
class CTyre //轮胎类
{
private:
int radius; //半径
int width; //宽度
public:
CTyre(int r, int w):radius(r), width(w) { }%构造函数
};
class CEngine //引擎类
{
};
注意: 新的构造函数初始化方式
CTyre(int r, int w):radius(r), width(w) { }
class CTyre //轮胎类
{
public:
CTyre() { cout << "CTyre contructor" << endl; }//轮胎类构造
~CTyre() { cout << "CTyre destructor" << endl; }//轮胎类析构
};
class CEngine //引擎类
{
public:
CEngine() { cout << "CEngine contructor" << endl; }//引擎类构造
~CEngine() { cout << "CEngine destructor" << endl; }//引擎类析构
};
class CCar //汽车类
{
private:
CEngine engine;
CTyre tyre;
public:
CCar( ) { cout << “CCar contructor” << endl; }
~CCar() { cout << "CCar destructor" << endl; }
};
int main(){
CCar car;
return 0;
}
程序的输出结果是:
CEngine contructor
CTyre contructor
CCar contructor
CCar destructor
CTyre destructor
CEngine destructor
构造时,先构造成员对象, 且按照封闭类中声明顺序构造,所以有:
CEngine contructor
CTyre contructor
CCar contructor
析构时,先析构封闭类,且成员对象析构顺序与构造顺序相反, 所以有:
CCar destructor
CTyre destructor
CEngine destructor
9 友元
9.1 友元函数
没详细看,有需要再看
一个类的友元函数
可以访问该类的私有成员
9.1 友元类
A是B的友元类 -> A的成员函数可以访问B的私有成员
Note:
友元类之间的关系,不能传递, 不能继承.i.e A 是 B的
友元类, B是C的友元类, A和C之间 无关系
10 this 指针
这个的作用, C++ prime plus书上举的例子比较好
简而言之: 指向调用成员函数的对象本身(因此静态成员函数不能用this指针)