C++引用、内联函数以及string类

引用

  • 引用是C++语法做的优化,引用的本质还是靠指针来实现的。
  • 引用相当于变量的别名。
  • 引用可以改变指针的指向,还可以改变指针所指向的值。

引用的基本规则:

声明引用的时候必须初始化,且一旦绑定,不可把引用绑定到其他对象;即引用必须初始化,不能对引用重定义
对引用的一切操作,就相当于对原对象的操作。

哪些地方可以引用?

  • 引用可以作形参(解决了传值和传地址的问题) void swap(int &a, int &b);
  • 做返回值(函数调用作返回值)

引用是否占用内存控件?

  • 从使用角度—不占用;
  • 从编译器----占用

指针和引用的区别? (面试题)

  • 引用不可为空,指针可以为空。
  • 指针在使用之前必须指向空,引用在声明的时候就必须初始化,指针可以改变指向,但是引用一旦初始化就不可解绑

左值引用和右值引用:

左值和右值:

  • 左值:左值是可以放在赋值号左边可以被赋值的值;左值必须要在内存中有实体;
  • 右值:右值当在赋值号右边取出值赋给其他变量的值;右值可以在内存也可以在CPU寄存器。
  • 一个对象被用作右值时,使用的是它的内容(值),被当作左值时,使用的是它的地址。

左值引用和右值引用:

  • 左值引用:左值引用的基本语法:type &引用名 = 左值表达式;
  • 右值引用:右值引用的基本语法type &&引用名 = 右值表达式;
    右值引用在企业开发人员在代码优化方面会经常用到。
    右值引用的“&&”中间不可以有空格。
#include 
using namespace std;
 
int main()
{
	cout << "-------引用左值--------" << endl;
	int a = 5;
	int &add_a(a);
 
	cout << "   a  =" << a <<"    "<<"    &a = "<<&a<< endl;
	cout << "add_a =" << add_a<<"    "<< "&add_a = " << &add_a << endl;
	cout << "-----------------------" << endl;
 
	cout << "-------引用右值--------" << endl;
	int b = 10;
	int &&add_b(b + 1);
	cout << "   b  =" << b << "    " << "    &b = " << &b << endl;
	cout << "add_b =" << add_b << "    " << "&add_b = " << &add_b << endl;
	add_b++;
	cout << "add_b++ =" << add_b << "    " << "&add_b++ = " << &add_b << endl;
	cout << "-----------------------" << endl;
 
	system("pause");
	return 0;
}

运行结果:
C++引用、内联函数以及string类_第1张图片

内联函数

  • 定义: 当函数被声明为内联函数之后, 编译器会将其内联展开, 而不是按通常的函数调用机制进行调用.
  • 优点: 当函数体比较小的时候, 内联该函数可以令目标代码更加高效. 对于存取函数以及其它函数体比较短, 性能关键的函数, 鼓励使用内联.
  • 缺点: 滥用内联将导致程序变慢. 内联可能使目标代码量或增或减, 这取决于内联函数的大小. 内联非常短小的存取函数通常会减少代码大小, 但内联一个相当大的函数将戏剧性的增加代码大小. 现代处理器由于更好的利用了指令缓存, 小巧的代码往往执行更快。
  • 结论: 一个较为合理的经验准则是, 不要内联超过 10 行的函数. 谨慎对待析构函数, 析构函数往往比其表面看起来要更长, 因为有隐含的成员和基类析构函数被调用!
    为什么用内联函数?(一般慎用)

函数虽然有这么多好处,但是写成函数有一个潜在的缺点:调用函数比求解等价表达式要慢得多。在大多数的机器上,调用函数都要做很多工作:调用前要先保存寄存器,并在返回时恢复,复制实参,程序还必须转向一个新位置执行。

C++中支持内联函数,其目的是为了提高函数的执行效率,用关键字 inline 放在函数定义(注意是定义而非声明,下文继续讲到)的前面即可将函数指定为内联函数,内联函数通常就是将它在程序中的每个调用点上“内联地”展开,假设我们将 max 定义为内联函数:

inline int max(int a, int b)
{
 return a > b ? a : b;
}

则调用: cout<

在编译时展开为: cout<<(a > b ? a : b)<

从而消除了把 max写成函数的额外执行开销

什么时候用内联函数?

  • 只有当函数只有 10 行甚至更少时才将其定义为内联函数

内联函数的使用:
关键字 inline 必须与函数定义体放在一起才能使函数成为内联,仅将 inline 放在函数声明前面不起任何作用。

如下风格的函数 Foo 不能成为内联函数:

inline void Foo(int x, int y);   // inline 仅与函数声明放在一起   
void Foo(int x, int y)
{
...
} 

而如下风格的函数 Foo 则成为内联函数:

void Foo(int x, int y);   
inline void Foo(int x, int y)   // inline 与函数定义体放在一起
{
 ...
} 

所以说,C++ inline函数是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。一般地,用户可以阅读函数的声明,但是看不到函数的定义。尽管在大多数教科书中内联函数的声明、定义体前面都加了 inline 关键字,但我认为 inline 不应该出现在函数的声明中。这个细节虽然不会影响函数的功能,但是体现了高质量C++/C 程序设计风格的一个基本原则:声明与定义不可混为一谈,用户没有必要、也不应该知道函数是否需要内联。

定义在类声明之中的成员函数将自动地成为内联函数,例如:

class A
{  
public:
 void Foo(int x, int y) { ... }   // 自动地成为内联函数  
} 

但是编译器是否将它真正内联则要看 Foo函数如何定义

内联函数应该在头文件中定义,这一点不同于其他函数。编译器在调用点内联展开函数的代码时,必须能够找到 inline 函数的定义才能将调用函数替换为函数代码,而对于在头文件中仅有函数声明是不够的

当然内联函数定义也可以放在源文件中,但此时只有定义的那个源文件可以用它,而且必须为每个源文件拷贝一份定义(即每个源文件里的定义必须是完全相同的),当然即使是放在头文件中,也是对每个定义做一份拷贝,只不过是编译器替你完成这种拷贝罢了。但相比于放在源文件中,放在头文件中既能够确保调用函数是定义是相同的,又能够保证在调用点能够找到函数定义从而完成内联(替换)。

但是你会很奇怪,重复定义那么多次,不会产生链接错误?

我们来看一个例子:

A.h :

class A
{
public:
 A(int a, int b) : a(a),b(b){}
 int max();
private:
 int a;
 int b;
};

A.cpp:

#include "A.h"
inline int A::max()
{
 return a > b ? a : b;
}

Main.cpp :

#include 
#include "A.h"
using namespace std;
inline int A::max()
{
 return a > b ? a : b;
}

int main()
{
 A a(3, 5);
 cout<<a.max()<<endl;
 return 0;
}

一切正常编译,输出结果:5

倘若你在Main.cpp中没有定义max内联函数,那么会出现链接错误:

error LNK2001: unresolved external symbol “public: int __thiscall A::max(void)” (?max@A@@QAEHXZ)main.obj
找不到函数的定义,所以内联函数可以在程序中定义不止一次,只要 inline 函数的定义在某个源文件中只出现一次,而且在所有源文件中,其定义必须是完全相同的就可以。

在头文件中加入或修改 inline 函数时,使用了该头文件的所有源文件都必须重新编译。

C++函数升级

struct升级

  • 定义变量(结构体+变量名)struct Node;
  • 结构体可以保存函数
  • 结构体可以继承
  • 结构体可以多态
  • 结构体可以封装(权限修饰符private protected public)
  • 结构体默认访问权限public private:不可被外部变量访问,只能结构体内部访问

class VS struct

  • class默认权限是privat,而struct是public
  • class类class定义的变量叫对象
  • class内部保存的变量称之为属性或者成员变量
  • class内部函数称之为方法

string类总结:

头文件:include

1.定义string:

#include 
#include 
using namespace std;
int main ( )
{
    string str;  //定义了一个空字符串str
    str = "Hello world";   // 给str赋值为"Hello world"
    char cstr[] = "abcde";  //定义了一个C字符串
    string s1(str);       //调用复制构造函数生成s1,s1为str的复制品
    cout<<s1<<endl;
    string s2(str,6);     //将str内,开始于位置6的部分当作s2的初值
    cout<<s2<<endl;
    string s3(str,6,3);  //将str内,开始于6且长度顶多为3的部分作为s3的初值
        cout<<s3<<endl;
    string s4(cstr);   //将C字符串作为s4的初值
    cout<<s4<<endl;
    string s5(cstr,3);  //将C字符串前3个字符作为字符串s5的初值。
    cout<<s5<<endl;
    string s6(5,'A');  //生成一个字符串,包含5个'A'字符
    cout<<s6<<endl;
    string s7(str.begin(),str.begin()+5); //区间str.begin()和str.begin()+5内的字符作为初值
    cout<<s7<<endl;
    return 0;
}

C++引用、内联函数以及string类_第2张图片

2.string的比较等操作

你可以用 ==、>、<、>=、<=、和!=比较字符串,可以用+或者+=操作符连接两个字符串,并且可以用[]获取特定的字符。

#include 
#include 
using namespace std;
int main()
{
    string str;
    cout << "Please input your name:"<<endl;
    cin >> str;
    if( str == "Li" )   // 字符串相等比较
        cout << "you are Li!"<<endl;
    else if( str != "Wang" )  // 字符串不等比较
        cout << "you are not Wang!"<<endl;
    else if( str < "Li")     // 字符串小于比较,>、>=、<=类似
        cout << "your name should be ahead of Li"<<endl;
    else
        cout << "your name should be after of Li"<<endl;
    str += ", Welcome!";  // 字符串+=
    cout << str<<endl;
    for(int i = 0 ; i < str.size(); i ++)
        cout<<str[i];  // 类似数组,通过[]获取特定的字符
    return 0;
}

C++引用、内联函数以及string类_第3张图片
3.string特性描述

可用下列函数来获得string的一些特性:

  • int capacity()const; //返回当前容量(即string中不必增加内存即可存放的元素个数)
  • int max_size()const; //返回string对象中可存放的最大字符串的长度
  • int size()const; //返回当前字符串的大小
  • int length()const; //返回当前字符串的长度
  • bool empty()const; //当前字符串是否为空
  • void resize(int len,char c); //把字符串当前大小置为len,多去少补,多出的字符c填充不足的部分
#include 
#include 
using namespace std;
int main()
{
    string str;
        if (str.empty())
        cout<<"str is NULL."<<endl;
        else
        cout<<"str is not NULL."<<endl;
    str = str + "abcdefg";
    cout<<"str is "<<str<<endl;
        cout<<"str's size is "<<str.size()<<endl;
       cout<<"str's capacity is "<<str.capacity()<<endl;
    cout<<"str's max size is "<<str.max_size()<<endl;
    cout<<"str's length is "<<str.length()<<endl;
    str.resize(20,'c');
    cout<<"str is "<<str<<endl;
    str.resize(5);
    cout<<"str is "<<str<<endl;
    return 0;
}

C++引用、内联函数以及string类_第4张图片

其他函数:

  • string &insert(int p,const string &s); //在p位置插入字符串s
  • string &replace(int p, int n,const char *s); //删除从p开始的n个字符,然后在p处插入串s
  • string &erase(int p, int n); //删除p开始的n个字符,返回修改后的字符串
  • string substr(int pos = 0,int n = npos) const; //返回pos开始的n个字符组成的字符串
  • void swap(string &s2); //交换当前字符串与s2的值
  • string &append(const char *s); //把字符串s连接到当前字符串结尾
  • void push_back(char c) //当前字符串尾部加一个字符c
  • const char data()const; //返回一个非null终止的c字符数组,data():与c_str()类似,用于string转const char其中它返回的数组是不以空字符终止,
  • const char *c_str()const; //返回一个以null终止的c字符串,即c_str()函数返回一个指向正规C字符串的指针, 内容与本string串相同,用于string转cons

你可能感兴趣的:(C++引用、内联函数以及string类)