C++11之explicit关键字、左值与右值、函数返回值当引用、结构体内存对齐

一、explicit关键字

作用是表明该构造函数是显示的, 而非隐式的.不能进行隐式转换! 类构造函数默认情况下即声明为implicit(隐式).注意没有implicit这个关键字。

// demo 16-1.c
#include <iostream>
#include <string>

using namespace std;

class student {
public:
	student(int _age)
	{
		age = _age;
		cout << "age=" << age << endl;
	}

	student(int _age, const string _name) 
	{
		age = _age;
		name = _name;
		cout << "age=" << age << "; name=" << name << endl;
	}

	~student()
	{

	}

	int getAge()
	{
		return age;
	}

	string getName() {
		return name;
	}


private:
	int age;
	string name;

};


int main(void) {
	student xiaoM(18);   //显示构造
	student xiaoW = 18;  //隐式构造  
	//student xiaoHua(19, "小花");   //显示构造
	//student xiaoMei = { 18, "小美" };  //隐式构造  初始化参数列表,C++11 前编译不能通过,C++11新增特性

	system("pause");
	return 0;
}

重点:
student xiaoM(18); //显示构造
student xiaoW = 18; //隐式构造
隐式构造 初始化参数列表,C++11 前编译不能通过,C++11新增特性
隐式构造可能会造成混淆,如像是普通赋值操作,所以想不支持隐式构造,那么就加上explicit。

二、左值与右值

按字面意思,通俗地说。以赋值符号 = 为界,= 左边的就是左值(lvalue),= 右边就是右值(rvalue)。

        int     a   =  666;
              左值    右值

        int     b   =  888;
              左值    右值

        int     c   =   a    +  b;
              左值    右值     右值

lvalue - 代表一个在内存中占有确定位置的对象(变量)(换句话说就是有一个地址)。
rvalue - 通过排他性来定义,每个表达式不是lvalue就是rvalue。因此从上面的lvalue的定义,rvalue是在不在内存中占有确定位置的表达式,而是存在在寄存器中。常量都是右值。
一般情况下,变量在左就是左值,在右就是右值。

所有的左值(无论是数组,函数或不完全类型)都可以转换成右值。

在linux中查看汇编代码:

#include<stdio.h>
int main()
{

        int a=1;
        int b=2;
        int c=3;
        c=a+b;
        printf("%d\n",c);
        printf("%p %p\n",&a,&b);
        return 0;
}

gcc test.c -S

 movl    $1, -12(%rbp)
 movl    $2, -8(%rbp)
 movl    $3, -4(%rbp)
三个变量存于栈rbp中
movl    -12(%rbp), %edx
movl    -8(%rbp), %eax
a、b的值移动到寄存器中
addl    %edx, %eax
movl    %eax, -4(%rbp)
将a和b相加的值放入eax寄存器中,将结果移动到c的位置

总结:为什么不能a+b=c?就是因为a+b是在寄存器中,在内存中没有一个确定的地址,所以不能作为左值。

特殊情况,静态变量返回引用可以作为左值。

三、函数返回值当引用

C++引用使用时的难点:

  1. 当函数返回值为引用时
    若返回栈变量,不能成为其它引用的初始值,不能作为左值使用

  2. 返回静态变量或全局变量
    可以成为其他引用的初始值

    即可作为右值使用,也可作为左值使用

  3. 返回形参当引用
    (注:C++链式编程中,经常用到引用,运算符重载)

#include <iostream>
#include <stdlib.h>
#include <stdio.h>

using namespace std;


int demo1() {
	int i = 0;
	//printf("i 的地址: %p, i=%d\n", &i, i);

	return i;
}

int &demo(int **addr) {
	int i = 666;
	*addr = &i;
	printf("i 的地址: %p, i=%d\n", &i, i);

	return i;
}

int &demo_static(int **addr) {
	static int i = 666;
	*addr = &i;
	printf("demo_static: i 的地址: %p, i=%d\n", &i, i);

	return i;
}

//3. 函数返回形参(普通参数)当引用,危险
int &demo3(int var) {
	var = 666;
	return var;
}

//4. 函数返回形参(引用)当引用,这里本身传入是引用,返回也是引用,相当于是同一个,不是局部变量,所以没事
int &demo4(int &var) {
	var = 666;
	return var;
}

int main(void) {
	int *addr = NULL;
	int ret = demo(&addr);	//这种情况没有问题,因为没有用引用去接,接到的就是一个值

	//第一种情况  函数返回局部变量引用不能成为其它引用的初始值
	//int &i1 = demo(&addr);  
	//i1 = 888;
	//printf("addr: %p i1=%d\n", addr, i1);

	//demo(&addr);
	
	//demo1();
	//printf("addr: %p i1=%d\n", addr, i1);

	//第二种情况 函数返回局部变量不能做为左值
	/*demo(&addr) = 888;
	printf("1. addr: %p  value: %d\n", addr, *addr);
	demo1();
	printf("2. addr: %p  value: %d\n", addr, *addr);
	*/

	//第三种情况  返回静态变量或全局变量可以成为左值或是其它引用的初始值
	//demo_static(&addr) = 888;
	int &i1 = demo_static(&addr);
	i1 = 888;
	printf("1. addr: %p  value: %d\n", addr, *addr);
	demo1();
	printf("2. addr: %p  value: %d\n", addr, *addr);
	demo_static(&addr);
	  
	system("pause");
	return 0;
}

总结上例:
第1、2种情况,都是改变的局部变量引用,那么当修改这个引用的值时,还能改变,但是因为局部变量一旦出了括号,生命周期就结束了,所以如果此时用另外一个函数可能分配的栈是同一个,那么此时这个函数就可能改变这个栈中的值,说明这个局部变量是不可靠的,随时都有可能被别人改变。

函数内部变量生存周期很短,生命周期就在函数内部,返回引用时不能用引用接收,因为有可能已经被释放掉了。但是本身是引用,或者返回的是静态变量/全局变量就没事,地址一直在用,不会被别人修改。

四、结构体内存对齐

#include <stdio.h>
#include <stdlib.h>

using namespace std;

struct A{
    char c;
    int i;
};


struct B{
    char c; 
    int i;  
    double d; 
};


struct C{
    char c;
    int i;
    double d;
    char c1;
};

int main(){
    printf("sizeof(A): %d\n", sizeof(struct A));		//8
    printf("sizeof(B): %d\n", sizeof(struct B));		//16
    printf("sizeof(C): %d\n", sizeof(struct C));		//24

    system("pause");
    return 0;
}

以上输出的结果并非实际成员占用的字节数,这就是结构体的内存对齐!

结构体内存对齐原因

1.平台原因(移植原因):
“不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些特定地址处取某些特定的数据,否则就会抛出硬件异常”。也就是说在计算机在内存读取数据时,只能在规定的地址处读数据,而不是内存中任意地址都是可以读取的。

2.效率原因:
正是由于只能在特定的地址处读取数据,所以在访问一些数据时,对于访问未对齐的内存,处理器需要进行两次访问;而对于对齐的内存,只需要访问一次就可以。 其实这是一种以空间换时间的做法,但这种做法是值得的。

结构体对齐规则

1.第一个成员在结构体变量偏移量为0 的地址处,也就是第一个成员必须从头开始。

2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数为编译器默认的一个 对齐数与该成员大小中的较小值。vs中默认值是8,Linux默认值为4(可以通过#pragma pack (N) 修改,使用#pragma pack(show) 可以查看对齐值,但修改时N的取 值只能设置成1, 2,4,8,16.

3.结构体总大小为最大对齐数的整数倍。(每个成员变量都有自己的对齐数)

4.如果嵌套结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是 所有最大对齐数(包含嵌套结构体的对齐数)的整数倍。

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值