c++类的特性探究(一)

类的定义可以使用struct关键字或者class关键字,两者唯一的区别就是默认的访问权限。访问权限与类的访问控制说明符有关:

public:定义在public后面的成员可以在整个程序里面被访问

private:定义在private后面的成员只能在类内部被访问

struct Sales_data{
public:

//public成员,可在整个程序里面被访问
    //构造函数,用于创建对象时初始化数据成员
    Sales_data()=default;
    Sales_data(const string &s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){}
    Sales_data(const string &s):bookNo(s){}
    Sales_data(istream&);
    //成员函数:关于sales_data对象的操作
    string isbn() const {return bookNo;}
    Sales_data & combine(const Sales_data&);
private:

//private成员,仅可在类内部被访问
    double avg_price()const{return units_sold?revenue/units_sold:0;};
    //数据成员:属于sales_data对象的属性
    string bookNo;
    unsigned units_sold=0;
    double revenue=0.0;
};

注:有些类成员可能定义在第一个说明符之外,那么此时如果使用struct关键字定义类,这些成员是public的,如果使用class关键字定义类,这些成员是private的。


友元:为了使得类的非成员接口函数能够访问类中的成员(包括公有私有),c++中引入友元一词,用以表示可以访问类的私有成员的友情函数。友元声明只能再类的内部,一般在顶部或者尾部,在函数前面加上friend关键字:

struct Sales_data{
friend Sales_data add(const Sales_data&,const Sales_data&);
friend ostream &print(ostream&,const Sales_data);
friend istream & read(istream&,Sales_data&);

public:
    //构造函数,用于创建对象时初始化数据成员
    Sales_data()=default;
    Sales_data(const string &s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){}
    Sales_data(const string &s):bookNo(s){}
    Sales_data(istream&);


    //成员函数:关于sales_data对象的操作
    string isbn() const {return bookNo;}
    Sales_data & combine(const Sales_data&);
private:
    double avg_price()const{return units_sold?revenue/units_sold:0;};
    //数据成员:属于sales_data对象的属性
    string bookNo;
    unsigned units_sold=0;
    double revenue=0.0;
};

//这三个即为友元
Sales_data add(const Sales_data&,const Sales_data&);
ostream &print(ostream&,const Sales_data);
istream & read(istream&,Sales_data&);

友元函数虽然已经在类内部有过一次声明,但是在类的外部还需一次声明,通常将类的声明和友元函数的声明放在同一个头文件。

友元函数也可以直接在类内部定义,但是还是需要在类的外部再次进行声明以使得友元函数可见。

除了可以声明友元函数外,还可以声明友元类以及某一个类中的部分成员函数为友元函数。

class screen{

friend class WindowMgr;//声明class WindowMgr是class screen的友元类,class WindowMgr的成员函数可以访问class screen的非公有成员

friend void window_mgr::clear(int);//声明window_mgr的成员函数clear为screen的友元函数,可以访问screen的非公有成员。

}

如果要将某个类的成员函数作为一个类的友元,我们需要严格的组织程序结构,以window_mg的clear函数作为screen的友元为例:

1.首先定义window_mgr类,其中声明clear函数,但是不能定义它,因为它将要用到的screen还没有声明。

2.定义screen类,包括对于window_mgr的clear函数的友元声明

3.定义clear函数,此时可以使用screen。



友情小贴士:封装的益处

1.确保用户代码不会无意间破坏封装对象的状态

2.类的具体实现细节可以随时改变,而不用调整用户级代码


类的可变数据成员:

有时候我们希望只改变对象的某一个数据成员(即便这个对象是const对象),此时可以在数据成员的声明里面加入关键字mutable。

class Screen{
public:
    void some_member()const;//改变可变数据成员access_ctr,统计成员函数被访问次数
private:
    mutable size_t access_ctr=0;//可变数据成员
};
void Screen::some_member()const{
    ++access_ctr;
}


关于返回*this的成员函数

有些成员函数需要返回调用对象以进行下一步操作,此时成员函数应该返回对于调用对象的引用,这样才可保证所有的操作都是作用在调用对象上面。例如:

myScreen.move(4,0).set('#');//移动光标同时设定光标显示的字符

此时如果move函数返回对象的引用,那么紧随其后的set函数也是作用在myScreen上面;如果move函数返回对象,那么set函数将会作用在所返回的对象副本上,与myScreen无关。对于返回常量非常量引用可以使用函数重载的方法解决。对于函数体中对于常量对象和非常亮对象不同的操作可以将不同功能的代码独立成一个成员函数,由于类中的成员函数都是内联函数,所以不会带来函数调用的额外开销。


类的作用域:

当我们要使用类中的成员时,我们必须在这之前使用类名和作用运算符指定具体的类。在类的外部定义成员函数时,一般函数名前面会有类名,因此函数体和参数列表都可以使用类的成员,但是函数的返回类型是在函数名之前的,如果函数返回值类型需要用到类中的成员,那么必须也要使用作用域运算符指定具体的类,即便使用的是同一个类。

编译器处理类定义的两个阶段:

1.首先编译全体成员的声明(包括成员函数)

2.直到类全部可见之后才编译函数体(函数声明中返回类型、参数列表所用到的名字必须在声明以前已经出现过)

成员函数的块中使用符号时的名字查找规则:

1.首先在函数内部查找该名字

2.如果函数内部没有其定义或者声明,那么在类中查找该名字(成员函数之前或者之后出现的都可以,只要在类中声明)

3.如果在类中仍然未找到该名字,那么在类外的全局作用域中寻找该名字(必须在使用该名字之前有过定义)

注:类型别名比较特殊,类的内部不允许覆盖外部的类型别名。并且即便是在类内部,定义类型别名也必须先定义后使用

综合上述规则可理清楚程序中有关符号作用域的问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值