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

C++知识点---const&&内联函数&&友元函数&&static&&N中拷贝构造的优化

2018-03-28 20:53 399 查看
更多知识点请点击链接C++知识点目录索引

1. const

关键字const

const使用规则:

const修饰变量,变量具有常属性,不可改

const修饰指针:

① const int* p; 指针指向内容不可修改

② int* const p; 指针不可修改

③ const int* const p; 指针和指向的内容都不可修改

const修饰函数

①常引用:修饰参数,防止参数被修改;例:bool operator==(const Date& d);

② 常函数:修饰类的成员函数,表示此函数不允许改变修改函数的数据成员

注意:const修饰的函数或变量,权限只可缩小不可放大

例:

void Func1() const   //常函数
{
_a=10;  //错误,数据不可修改
Func2(); //const不能调用非const,权限放大
}
void  Func2()
{
Func1();//非const可调用const,权限缩小
}


const修饰成员函数

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



2. 内联函数

定义

以inline修饰的函数叫做内联函数,编译时C++编译器会调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。

特点

① 类的成员函数默认为内联函数

② 以空间换取时间,省去函数栈帧的开销

③ inline必须函数定义放在一起,才能成为内联函数,仅将inline放在声明前是不起不作用的。

④ 内部代码较短,编译器会自动优化 当函数体内代码很长或有递归时,内联不会展开

优缺点

优点:当函数体比较小的时候, 内联该函数可以令目标代码更加高效. 对于存取函数以及其它函数体比较短, 性能关键的函数, 鼓励使用内联.

缺点:滥用内联将导致程序变慢. 内联可能使目标代码量或增或减, 这取决于内联函数的大小. 内联非常短小的存取函数通常会减少代码大小, 但内联一个相当大的函数将戏剧性的增加代码大小. 现代处理器由于更好的利用了指令缓存, 小巧的代码往往执行更快。

使用原则

① 一个较为合理的经验准则是, 不要内联超过 10 行的函数.

② 内联那些包含循环或 switch 语句的函数常常是得不偿失 (除非在大多数情况下, 这些循环或 switch 语句从不被执行).

③ 有些函数即使声明为内联的也不一定会被编译器内联,比如递归函数

3. 内联函数与宏

原因:

考虑到宏的一些缺点,在C++中,建议使用 内联替代宏;内联函数替代宏即能达到宏的效率,在某些问题上也能自由访问类的数据成员;

宏的优缺点:

优点:增强代码的复用性,增强性能

缺点:不方便调试;可读性差,容易误用,可维护性差;无类型安全检查

在书《高质量程序设计指南——C++/C语言》中这样解释到:



4. 友元函数

定义

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

特点

① 友元函数不是类的成员函数,必须采用全局

② 友元函数在一定程度上破坏了类的封装性,可以从类外面访问私有成员

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

例子

class  AA
{
friend void  Show(const AA& A);//声明
public:
private:
int _a;
int _b;
}
//全局(特点 ①)
void Show()
{
cout<<A._a<<end;//访问了私有成员,特点②
cout<<A._b<<endl;
}

//友元类(特点 ③)
class  AA
{
// BB是AA 的友元,所以 BB可以访问AA的所有成员。
friend class BB;
private :
int _a1;
int _a2 ;

};
class BB
{
public :
void Display ()
{
cout<<_b1<< endl;
cout<<_b2<< endl;

// 定义为友元类后,可以访问AA类对象的所有成员
cout<<_A. _a1<<endl ;
cout<<_A. _a2<<endl ;

}
private :
int _b1 ;
int _b2 ;
AA   _A;
}


应用场景—输入输出运算符的重载

输入输出指的是从标准输入或者从标准输出,可以将其看作是数据的流向:数据流向某一区域,而这一区域所指的就是标准输入(硬盘,内存等)或标准输出(显示器,文件等)

class Date
{
public :
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& out , const Date& d)
{

//对象d流向ostream& out,返回out,enl流向out
out<<"year:" <<d. _year<<endl ;
out<<"month:" <<d. _month<<endl ;
out<<"day:" <<d. _day<<endl <<endl;
return out ;
}

//输出运算符的重载
istream & operator>> ( istream& in , Date& d)
{
cout<<" 请分别输入年月日: "<<endl ;
in>>d ._year;
in>>d ._month;
in>>d ._day;
return in ;
}
void Test ()
{
Date d1 ;
cin>>d1 ;
cout<<d1 ;
}


5. 类的静态成员

成员变量

① 类里面static修饰的变量称为静态类成员;

② 静态类成员属于类当中的所有对象

class  Date
{
public:
Date()
{
cout<<"Date()"<<endl;
++count;
}
private:
int _year;
int _month;
int _day;
static size_t  count;//静态成员变量,统计日期创建的个数

}

size_t  Date::count=0;//初始化时注意指明类域


成员函数

① 调用静态成员函数时,使用类型::作用域访问符直接调用静态成员函数

② 静态成员函数没有隐含的this指针,只能访问静态成员变量

③ 静态成员函数不能访问非静态成员变量,不允许权限放大

④ 非静态成员函数可以访问静态成员变量,权限缩小

class Date
{
public :
Date ()
{
cout<<"Date ()" <<endl;
++ count;
}
void Display ()
{
cout<<"year:" <<_year<< endl;
cout<<"month:" <<_month<< endl;
cout<<"day:" <<_day<< endl;
}
// 静态成员函数
static void PrintCount()
{
cout<<"Date count:" <<count<< endl;//只能访问静态成员变量
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
static size_t count; // 静态成员变量,统计创建时间个数
};
// 定义并初始化静态成员变量
int Date::sCount = 0;
void Test ()
{
Date d1 ,d2;
// 访问静态成员
Date::PrintCount ();
}


6. 拷贝构造函数的优化

函数中调用中拷贝构造原则:

对象作返回值传值时,生成匿名对象,此匿名对象调用拷贝构造函数

对象作参数传值时,传参过程中形成匿名对象,此匿名对象调用拷贝构造函数

优化原则

表达式为一条语句时,构造和拷贝构造进行合并

拷贝构造出来的匿名对象返回马上作为下一个拷贝构造函数的参数时,两次拷贝构造函数合并成一个

当对象已经创建出来,需要构造新的对象或匿名对象时,不会发生优化

例:

class Date
{
public:
Date()
{
cout << "Date()" << endl;
}
Date(const Date& d)
{
cout << "Date(const Date& d)" << endl;
}
Date& operator =(const Date& d)
{
cout << "Date& operator=(const Date& d)" << endl;
return *this;
}
~Date()
{
cout << "~Date()" << endl;
}
};

// 1.Date 对象做参数传值 & 传引用
void fun1(Date d) //void fun1(Date& d)
{}

// 2.Date 对象做返回值传值 & 传引用
Date& fun2() // Date& fun2()
{
Date d;
return d;
}

// 3.Date 对象做临时返回值传值 &传引用(编译器优化问题)
Date fun3() // Date& fun3()
{
return Date();
}

int main()
{
// 场景
Date d1;
fun1(d1);

// 场景2
Date d2 = fun2();

// 场景3
Date d3;
d3 = fun3();

system("pause");
return 0;
}


分析:

场景1

参数为对象:一次构造,一次拷贝构造,两次析构(拷贝构造原则2,优化原则2)

参数为引用:一次构造,一次拷贝构造,一次析构



场景2

返回值为对象:一次构造,一次拷贝构造,两次析构(拷贝构造原则1,优化原则2)

返回值为引用:一次构造,一次拷贝构造,一次析构



场景3

对象作返回值传值: 两次构造,一次赋值运算符重载(拷贝构造原则1,优化原则1)

对象作返回值传引用:两次构造,一次赋值运算符重载



7. 几个习题

Test1中调用了 2 次AA的拷贝构造函数, 1 次AA的赋值运算符函数的重载。

Test2中调用了2 次AA的拷贝构造函数, 0 次AA的赋值运算符函数的重载。

Test3中调用了3 次AA的拷贝构造函数, 0 次AA的赋值运算符函数的重载。

class AA
{};
AA f (AA a)
{
return a ;
}
void Test1 ()
{
AA a1 ;
a1 = f(a1);
}
void Test2 ()
{
AA a1 ;
AA a2 = f(a1);
}

void Test3 ()
{
AA a1 ;
AA a2 = f(f(a1));
}


分析:

Test1中调用了 2 次AA的拷贝构造函数, 1 次AA的赋值运算符函数的重载。



Test2中调用了2 次AA的拷贝构造函数, 0 次AA的赋值运算符函数的重载。



Test3中调用了3 次AA的拷贝构造函数, 0 次AA的赋值运算符函数的重载。

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