您的位置:首页 > 编程语言 > C语言/C++

C++ 点滴积累(3)

2011-10-22 21:45 218 查看
1.析构函数的调用时机:

1)} 或 文件尾

2)delete(有条件的)

3)catch()

当中途退出程序时(exit()、abort() )不调用析构函数

2.在函数内部定义的类叫内部类,内部类的成员函数只能是内联函数。

3.静态成员(类属性)

静态数据成员–用关键字static声明;

–该类的所有对象维护着同一份拷贝;

–不能在构造函数中初始化,应在类外单独进行,必须在生成对象之前用(::)来指明所属的类。

静态成员函数

–类外代码可以使用类名和作用域操作符(::)来调用静态成员函数;

–静态成员函数只能引用属于该类的静态数据成员或静态成员函数。

//具有静态数据、函数成员的Point类
#include <iostream>
using namespace std;
class Point//Point类声明
{
public://外部接口
Point(int xx=0, int yy=0) {X=xx;Y=yy;countP++;}
Point(Point &p);//拷贝构造函数
int GetX() {return X;}
int GetY() {return Y;}
static void GetC()
{cout<<" Object id="<<countP<<endl;}
private://私有数据成员
int X,Y;
static int countP;
};

Point::Point(Point &p)
{
X=p.X;
Y=p.Y;
countP++;
}
int Point::countP=0;
void main()//主函数实现
{
Point A(4,5);  //声明对象A
cout<<"Point A,"<<A.GetX()<<","<<A.GetY();
A.GetC();     //输出对象号,对象名引用
Point B(A);   //声明对象B
cout<<"Point B,"<<B.GetX()<<","<<B.GetY();
Point::GetC(); //输出对象号,类名引用
}
#include <iostream>
using namespace std;

class A
{
public:
A(int xx):x(xx){}
static void f(A a);
private:
int x;
};

void A::f(A a)
{
/*
静态成员函数对对象成员的访问是错误的,
因为它是类属性,并不知晓对象的事。
*/
//cout<<x; //对x的引用是错误的
cout<<a.x; //正确
}

void main()
{
A obj(2);
obj.f(obj);
}

静态成员的性质

静态数据成员

–对象的存储空间中不包含静态成员的位置;

–尽管可能是私有成员,也必须在类外定义和初始化;

–若是对象成员,则必然调用构造函数

静态成员函数

–静态成员函数不含有this 指针;

–静态数据成员或静态成员函数不继承;

静态成员函数不可以定义为虚函数;

静态成员函数不必重载也不必为常函数。

5.友元

– 友元是C++提供的一种破坏数据封装和数据隐藏的机制。

– 通过将一个模块声明为另一个模块的友元,一个模块能够使用到另一个模块中本是被隐藏的信息。

– 可以使用友元函数和友元类。

– 友元不是组合,定义和调用时不受类作用域限制。

== 友元函数

友元函数是在类声明中由关键字friend修饰的非成员函数,在它的函数体中能够通过对象名访问private 和protected成员;

友元函数可以是类外的普通函数,也可以是另一类的成员函数,

作用:增加灵活性,使程序员可以在封装和效率间合理选择。

若访问对象的成员,必须前缀对象名。这样可以访问类中的任何访问权限的成员。

//使用友元函数计算两点距离
#include <iostream>
#include <cmath>
using namespace std;
class Point//Point类声明
{
public://外部接口
Point(int xx=0, int yy=0) {X=xx;Y=yy;}
int GetX() {return X;}
int GetY() {return Y;}
friend double Distance(Point &a, Point &b);
private://私有数据成员
int 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<<"The distance is "<<d<<endl;
return 0;
}


== 友元类

若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员(全方位开放)。

声明语法:将友元类名在另一个类中使用friend修饰说明

友元关系是单向的如果B类是A类的友元,则B类的函数可以访问A类的私有和保护数据,但A类的成员函数却不能访问B类的私有、保护数据。(孙悟空钻进铁扇公主肚里。)

友元关系不能传递如果B类是A类的友元,C类是B类的友元,若没特别声明,则C类和A类无友元关系。(朋友的朋友不见得是朋友!)

友元关系不能继承如果B类是A类的友元,B类的子类不会自动成为A类的友元类。(借来的东西不是遗产。)

#include <iostream>
using namespace std;
class A
{
friend class B;
public:
A(int xx = 1):x(xx){}
void Display()
{cout<<x<<endl;}
private:
int x;
};
class B
{
public:
void Set(int i);
void Display();
private:
A a;
};

void B::Set(int i)
{
//访问组合对象的私有成员正是友元的“特长”。
a.x=i;
}
void B::Display()
{
a.Display();
}

int main()
{
B obj;
obj.Display();
return 0;
}
6.“常”的种类

常引用:被引用的对象不能被更新。const 类型说明符&引用名

常对象:必须进行初始化,且不能被更新。类名const 对象名

常对象只能调用常成员函数;在常函数和非常函数并存时,非常对象只能调用非常函数,在只有常函数时,非常对象可以调用常函数
常成员(包括常数据成员和常函数成员)类型说明符 const 数据成员名 , 返回类型 函数成员名()const

常成员函数

–使用const关键字说明的函数。

–常成员函数不更新对象的数据成员。

–常成员函数说明格式:

类型说明符函数名(参数表)const;

这里,const是函数类型的组成部分,因此在实现部分也要带const关键字。

–const关键字可以被用于参与对重载函数的区分

–派生时可以用const防止基类的函数被子类覆盖;

–const不能用于构造、析构(程序执行不警告)

常对象仅能调用它的常成员函数。

#include<iostream>
using namespace std;
class R
{
public:
R(int r1, int r2){R1=r1;R2=r2;}
void print();
//const关键字可以被用于参与对重载函数的区分
void print() const;
private:
int R1,R2;
};

void R::print()
{
cout<<R1<<":"<<R2<<endl;
}

//const是函数类型的组成部分,因此在实现部分也要带const关键字。
void R::print() const
{
cout<<R1<<";"<<R2<<endl;
}
void main()
{
R a(5,4);
a.print(); //调用void print()
const R b(20,52);
//常对象仅能调用它的常成员函数。
b.print(); //调用void print() const
}


常成员函数的实质:

在常函数:返回类型函数成员名()const 中

const 实质上是用于限定该函数所含的this指针的,即变成了“指向常对象的指针”。于是对象在调用常函数时,尽可放心。

这也回答了为什么只有非静态成员函数(因为静态成员函数不含有this 指针)才能是常函数。

常数据成员

–使用const说明的数据成员。常数据成员/常引用是通过用初始化列表完成赋值的。

#include<iostream>
using namespace std;
class A
{
public:
A(int i);
void print();
const int& r;
private:
const int a;
static const int b; //静态常数据成员
};
const int A::b=10;
A::A(int i):a(i),r(a) {}
void A::print()
{
cout<<a<<":"<<b<<":"<<r<<endl;
}
void main()
{
/*
建立对象a和b,并以100和0作为初值,分别调用构造函数,
通过构造函数的初始化列表给对象的常数据成员赋初值
*/
A a1(100),a2(0);
a1.print();
a2.print();
}
运行结果:

100:10:100

0:10:0

常数组:数组元素不能被更新。类型说明符 const 数组名[大小]

指向常量的指针和常指针:

指向常量的指针const int a=99;const int* p = &a;亦可写为:int const * p = &a;

常指针(相当于引用)char * const p = “ABCDE”;

是指针类型的常量;

若声明指针为常量,则指针本身的值不能被改变,即该指针始终指向一个地方,不可改变。常用来作形参接受数组名实参,于是该指针便老老实实地指着该数组不变。这样指针便不可能丢失数组。

指向常量的常指针(很少用)const char *const p = "John";

7.必须使用初始化列表的场合:

(1)类成员是引用

(2)类含有常数据成员

(3)被组合的类的构造函数带参,而由组合类创建对象时

(4)基类的构造函数带参,子类创建对象时

8.“常”与函数的关系

常参数:函数的形参若是指针或引用,就会将实参完全暴露给子函数。若仅仅为了大量传送而非双向传递数据,可用const修饰引用和指针。

const 类型说明符&引用名

const 类型说明符*指针名

常函数:封锁了函数的“写”功能,不能更新数据成员。

常返回:函数返回的是个常量。通常这个常量是指针或引用。

9.改变常对象的数据

一个对象一旦定义为常对象就意味着其所的数据成员都不可更改了,可是在实际应用中会有这种情况:一个存单类,记录了户主、金额、期限等信息,当发生抵押时,户主要变化,其它项不动。

为了适应这种需求,C++提供mutable 关键字,用于修饰常对象中的可变数据成员,使得常函数可以修改它,非常函数也可以用。

mutable 关键字可用于非静态、非常、非引用的数据成员。

#include <iostream>
#include <string>
using namespace std;
class scrollbar
{
private:
int size; //related to constness
mutable string owner; //可变数据成员
public:
scrollbar(int sz, string own) : size(sz), owner(own){ }
void setSize(int sz) //changes size
{
size = sz;
}
void setOwner(string own) const //是竟然可以修改的常函数
{
owner = own;
}
int getSize() const //returns size
{
return size;
}
string getOwner() const //returns owner
{
return owner;
}
};

void main()
{
const scrollbar sbar(60, "Window1"); //生成常对象
cout<< sbar.getSize() << ", " << sbar.getOwner() << endl<< endl;
// sbar.setSize(100); //can't do this to const obj
// 常对象调用常函数,修改mutable数据成员
sbar.setOwner("Window2");
cout<< sbar.getSize() << ", " << sbar.getOwner() << endl<< endl;
}


10.编译预处理命令

1)#include 包含指令

–将一个源文件嵌入到当前源文件中该点处。

–#include<文件名>

按标准方式搜索,文件位于C++系统目录的include子目录下。

–#include"文件名"

首先在当前目录中搜索,若没有,再按标准方式搜索。

2)#define 宏定义指令

–定义符号常量,很多情况下已被const定义语句取代。

–定义带参数宏,已被内联函数取代。

#undef

–终止由#define定义的宏,使之不再起作用。

3)条件编译指令#if 和#endif

#if常量表达式

//当“常量表达式”非零时编译

程序正文

#endif

4)条件编译指令#else

#if 常量表达式

//当“常量表达式”非零时编译

程序正文1

#else

//当“常量表达式”为零时编译

程序正文2

#endif

5)条件编译指令#ifdef

#ifdef标识符

程序段1

#else

程序段2

#endif

如果“标识符”经#defined定义过,且未经# undef删除,则编译程序段1,否则编译程序段2。

6)条件编译指令#ifndef

#ifndef标识符

程序段1

#else

程序段2

#endif

如果“标识符”未被定义过,则编译程序段1,否则编译程序段2。

7)使用条件编译的头文件

//head.h

#ifndefHEAD_H //避免了重复编译

#define HEAD_H //此标识符被称为“哨兵”



class Point

{



}



#endif
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: