C++有关 const & 内敛 & 友元&静态成员那些事


C++中有关 const & 内敛 & 友元&静态成员 的用法比较杂乱,算是C++中一个麻烦的部分。现速速的对它们做大致的总结,了解它们当中常见的用法和陷阱。

const修饰的成员函数&对象


在成员函数后面加const,const修饰this指针所指向的对象,也就是保证调用这个const成员函数的对象在函数内不会改变

 /*************************************************************************
   > File Name: 2.cc
   > Author: tp
   > Mail: 
   > Created Time: Tue 19 Jun 2018 05:48:12 PM CST
  ************************************************************************/
 
 #include 
 #include 
 #include 
 using namespace std;
 
 class Date
 {
     public :
         Date(int y=0, int m =0, int d=0)
             :_year(y),_month( m),_day( d){ }
 
         void Display ( )
         {
             cout<<" Display ( ) "  <<endl;
             cout<<" year:"  <<_year<< endl;
             cout<<" month:"  <<_month<< endl;
             cout<<" day:"  <<_day<<     endl<<endl ;
         }
         void Display ( ) const
         {
             cout<<" Display ( )  const"  <<endl;
             cout<<" year:"  <<_year<< endl;
             cout<<" month:"  <<_month<< endl;
             cout<<" day:"  <<_day<< endl<<endl;
         }
     private :
         int _year ; //
         int _month ; //
         int _day ; //
 };
 int main( )
 {
     Date d1;
     d1.Display ( ) ;
     const Date d2;
     d2.Display();
     return 0;
 }

常见情景:

1. const对象可以调用非const成员函数和const成员函数吗?

  只能调用const成员函数。因为是const对象,它就要求对象内部数据初始化好不会改变,所以其相应成员函数也应由const修饰。

 #include 
 #include 
 #include 
 using namespace std;
 
 class Date
 {
     public :
         Date(int y=0, int m =0, int d=0)
             :_year(y),_month( m),_day( d){ }
 

         void Display ( ) 
         {
             cout<<" Display ( ) "  <<endl;
             cout<<" year:"  <<_year<< endl;
             cout<<" month:"  <<_month<< endl;
             cout<<" day:"  <<_day<< endl<<endl;
         }
     private :
         int _year ; //
         int _month ; //
         int _day ; //
 };
 int main( )
 {
   // Date d1;
   //  d1.Display ( ) ;
     const Date d2;
     d2.Display();
     return 0;
 }
//结果:报错!


2. 非const对象可以调用非const成员函数和const成员函数吗?

  都可以调用。调用非const成员函数没有争议,当非const对象调用const成员函数时,对非const对象来说,这其实是一个权限的缩小,是可以完成的。

 #include 
 #include 
 #include 
 using namespace std;
 
 class Date
 {
     public :
         Date(int y=0, int m =0, int d=0)
             :_year(y),_month( m),_day( d){ }
 
         void Display ( ) const
         { 
             cout<<" Display ( )  const"  <<endl;
             cout<<" year:"  <<_year<< endl;
             cout<<" month:"  <<_month<< endl;
             cout<<" day:"  <<_day<< endl<<endl;
         }
     private :
         int _year ; //
         int _month ; //
         int _day ; //
 };
 int main( )
 {
     Date d1;
     d1.Display ( ) ;
 //  const Date d2;
 //  d2.Display();
     return 0;
 }
//结果:成功调用

3. const成员函数内可以调用其它的const成员函数非const成员函数吗?

  在const成员函数中是不能调用非const成员函数。道理同上
4. 非const成员函数内可以调用其它的const成员函数非const成员函数吗?

  非const成员函数既可以掉非const成员函数 也可以调用const成员函数(道理相似,非const成员函数相当于一次权限缩小)

内敛


关于内敛,内敛就是以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方插入代码进行展开,没有
函数压栈的开销,内联函数提升程序运行的效率。

关于内敛的几点注意:

·inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的的函数不适宜使用内联。
·inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。
·inline必须和函数定义放在一起,才能成为内联函数,仅仅将inline关键字放到声明前是不起作用的。
·定义在类内的成员函数默认为内联函数。

·debug版本下的内敛函数不展开,所以它可以像普通函数那样进行调试。

class Date
{
public :
    void Func () // 定义在类内部默认为内联函数
    {}
    void Display ();
private :
    int _year ; //
    int _month ; //
    int _day ; //
};
inline void Date::Display ()  // 成员函数定义为内联
{
    cout<<"year:" <<_year<< endl;
    cout<<"month:" <<_month<< endl;
    cout<<"day:" <<_day<< endl;
}
inline void Test() // 全局函数定义为内联
{}

 内敛和宏区别:

·最大的区别在于宏是在预处理阶段处理,而内敛是在编译阶段来进行处理。

·预处理阶段发生宏替换,但替换仅仅是单纯的文本替换,不作类型安全的检查,同时它也不能去访存私有的数据成员;

·而内敛在编译阶段进行的处理,它有类型安全的检查(即调用一个内联函数时,编译器首先确保正确,即所有的参数类型必须是正确的或者编译器必须能够将类型转换为正确类型,并且返回值在目标表达式里应该是正确的类型或者可以改变为正确的类型),同时也可以访存私有的数据成员。

总结来说就是内敛可以像宏一样可以提高代码执行效率,但内敛还可以进行类型安全检查,访问私有数据成员,所以可尽量用内敛去替代宏。

友元


在C++中友元函数允许在类外访问该类中的任何成员,就象成员函数一样,友元函数用关键字friend说明。

关于友元:

1. 友元函数不是类的成员函数。
2. 友元函数可以通过对象访问所有成员,私有和保护成员也一样。

一个典型的例子:

重载自定义类型的输入(>>) 输出(<<)

class Date
{
public :
// operator<<不能为成员函数
// 返回值定义为输入输出流的引用。
friend ostream& operator<< ( ostream& os , const Date& d );
friend istream& operator>> ( istream& is , Date& d);
private :
    int _year ; //
    int _month ; //
    int _day ; //
};
ostream& operator<<( ostream& os , const Date& d)
{
    os<<"year:" <endl ;
    os<<"month:" <endl ;
    os<<"day:" <endl;
    return os ;
}
// cin.operator<<(cin, d1)
istream& operator>> ( istream& is , Date& d)
{
    cout<<" 请分别输入年月日: "<<endl ;
    is>>d ._year;
    is>>d ._month;
    is>>d ._day;
    return is ;
}
void Test ()
{
    Date d1 ;
    cin>>d1 ;
    cout<<d1 ;
}

为什么说<<, >>操作符重载函数 不能是成员函数?

 class Date
 {
     public :
         ostream& operator<< (ostream& os) ;
         istream& operator>> (istream& is) ;
     private :
         int _year ; //
         int _month ; //
         int _day ; //
 } ;
 ostream& Date::operator<<(ostream& os)
 {   
     os<<" year:"  << _year<<endl ;
     os<<" month:"  << _month<<endl ;
     os<<" day:"  << _day<endl;
     return os ;
 } 
 // cin.operator<<( cin, d1) 
 istream& Date::operator>> (istream& is)  
 {   
     cout<<"  请分别输入年月日: " <<endl ;
     is>>_year;
     is>>_month;
     is>>_day;
     return is ;
 } 

 void Test ( )
 {
     Date d1 ;
     d1>>cin;
     d1<<cout;
 }

不难注意到最后我们调用>> <<操作符时,将成为一种怪异的操作!估计是难以让人接受的。

究其原因,不难发现其实是由this指针导致的。因为this指针会默认会用成员函数的第一个位置,其它传进来的参数(这里的ostream 、istream)都只能溜到它后面去玩,所以才会致使cin、cout跑到“>>”,"<<"的右边去。想要像正常情况那样使用cin cout,我们就只有不把它们写成成员函数了,这样就没有this指针来抢位子了。但是,这无疑又将引来一个问题:由于函数需要访问传入进来的输入、输出对象,但此时对象的成员是被封装的,函数访问必然就会失败。

所以最后要解决这个问题,我们就只有将“opetator >>”,"operator<<"函数定义为Date类的友元函数了,这样便可通过对象来访问其中的数据成员了,最后完成目的。

从这里也容易看出用友元,其实是一种破坏封装的行为

除此友元函数之外还有友元类

友元类:整个类可以是另一个类的友元。友元类的每个成员函数都是另一个类的友元函数,都可访问另一个类中的保护或私有数据成员。

静态成员


 类里面static修饰的成员,成为静态类成员。它最大的特点就是:类的静态成员是该类型的所有对象对象所共享。

注意:静态成员函数没有隐含的this指针参数,所以可以使用类型::作用域访问符直接调用静态成员函数。

 #include 
 #include 
 #include 
 using namespace std;
 class Date
 {
     public :
         Date()
         { 
             cout<<"Date( )"<<endl;
             ++dCount;
         }
         static void printCount( )
         {   
             cout<<"Date::dCount:"<endl;;
         }
     private :
         static int dCount;
 } ;
 //定义并初始化静态成员
 int Date::dCount = 0;
 void Test( )
 {
     Date d1 ;
     d1.printCount( );  //1
     Date d2 ;
     Date d3 ;
     //可以直接用类名加类域限定符调用
     Date::printCount( ); //3
 }

 关于静态成员一点思考:

1. 静态成员函数可以访问非静态的成员吗?

  不可以,因为静态成员函数没有this指针。由于非静态数据成员是存储在对象中,成员函数访问数据成员要借助this指针,所以没有this指针的静态成员函数不能访问非静态数据成员。
2. 非静态的成员函数可以访问静态成员吗?

  可以的,静态数据成员存储在地址空间的数据段,它为该类的所有对象共享,这些对象的成员函数自然也可以访问。