• 剑指offer试题整理1


    1、定义一个空的类型,里面没有任何成员变量和成员函数。对该类型求sizeof,得到的结果是什么?

    答案:1.

    为什么不是0?

    空类型的示例中不包含任何信息,本来求siezof应该是0,但是当我们声明改类型的实列是时候,它必须再内存中占有一定的空间,否则无法使用这些实例。
    对于至少占用多少内存,这由编译器决定。
    在Visual Studio中,每个空类型的实例都占用一字节的空间。
    在这里插入图片描述

    如果在该类型中添加一个构造函数和析构函数,再对该类型求sizeof,得到的结果优势多少?

    还是1。
    调用构造函数和析构函数只需要知道函数的地址即可,而这些函数的地址只与类型相关,而与类型的示例无关,编译器也不会因为这两个函数而再示例内添加任何额外的信息。
    在这里插入图片描述

    那添加一个析构函数呢?如果把析构函数标记为虚函数呢?

    C++编译器一旦发现一个类型中由虚函数,就会为该类型生成虚函数表,并在该类型的每一个示例中添加一个指向虚函数表的指针。
    在32位的机器上,一个指针占4字节,因此求sizeof得到4;如果是在64位的机器,则一个字节占8字节的空间,因此求sizeof得到8。

    在这里插入图片描述

    2、

    给出以下代码,分析编译器运行的结果:
    A、编译错误;
    B、编译成功,运行时程序崩溃;
    C、编译运行正常,输出10;

    class A
    {
    private:
    	int value;
    public:
    	A(int n){value = n;}
    	A(A other){value = other.value;}
    
    	void Print(){std::cout << value << std::endl;}
    
    };
    
    int _tmain(int argc,_TCHAR *argv[])
    {
    	A a = 10;
    	A b = a;
    	b.Print();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在上述代码中,复制构造函数A(A other)传入的参数是A的一个实例。由于是传值参数,我们把形参复制到实参会调用复制构造函数。因此,如果允许复制构造函数传值,就会在复制构造函数内调用复制构造函数,就会形成永无休止的递归调用从而导致栈溢出。因此,C++的标准不允许复制构造函数传值参数,在 Visual Studio和 GCC中,都将编译出错。要解决这个问题,我们可以把构造函数修改为A(const A& other),也就是把传值参数改成常量引用。

    在这里插入图片描述
    在这里插入图片描述

    3、赋值运算符函数

    题目:如下为类型CMyString 的声明,请为该类型添加赋值运算符函数。

    class CMyString
    {
    public:
    	CMyString(char *pData = nullptr);	
    	CMyString(const CMyString& str);
    	~CMyString(void);
    	
    private:
    	char* m_pData;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    经典解法:

    CMyString& CMyString::operator =(const CMyString &str)
    {
    	//判断自赋值
    	if(this == &str)
    		return *this;
    
    	//先释放左值
    	delete[]m_pData;
    	m_pData = nullptr;
    
    	//开辟右值空间大小
    	m_pData = new char[strlen(str.m_pData) + 1];
    
    	//拷贝赋值
    	strcpy_s(m_pData,strlen(str.m_pData) + 1,str.m_pData);
    
    	//返回引用
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    考虑异常安全性的解法

    在经典解法中,我们在分配内存之前先调用了delete 释放了实例 m_pData 的内存。但如果此时的内存不足,导致new char 抛出异常,则 m_pData 将是一个空指针,这样很容易导致程序崩溃。
    即,一旦在赋值运算符函数内部抛出一个异常,CMyString的实例不再保持有效的状态,这就违背了安全性原则。

    要想在赋值运算符函数中实现异常安全性,有两种方法:
    一种简单的办法是我们先用new 分配新内容,再用 delete释放已有的内容。这样只在分配内容成功之后再释放原来的内容,也就是当分配内存失败时我们能确保 CMyString的实例不会被修改。
    我们还有一种更好的办法,即先创建一个临时实例,再交换临时实例和原来的实例。下面是这种思路的参考代码:

    	CMyString& CMyString::operator =(const CMyString &str)
    	{
    		if(this != &str)
    		{
    			//创建一个临时实例strTemp,在当前{}结束后,会自动调用自己的析构函数释放相关内存
    			CMyString strTemp(str);
    
    			//把strTemp.m_pData 和实例自身的m_pData进行交换  当临时变量strTemp析构时,因为进行了交换,所以会释放原来p_Data的内存
    			char* pTemp = strTemp.m_pData; 
    			strTemp.m_pData = m_pData;
    			m_pData = pTemp;
    		}
    
    		return *this;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4、在C/C++中,struct和class的区别?

    C++中类和结构体的区别:

    默认的防控属性(访问控制)不同,struct是public的,而class是private的。
    
    • 1

    C中结构体和C++结构体区别:

  • 相关阅读:
    基于西门子PLC的自动门控制装置设计
    接口幂等性问题
    【微信小程序】image组件的4种缩放模式与9种裁剪模式
    JVM基础 -> 什么是STW?
    HINet: Half Instance Normalization Network for Image Restoration
    Zookeeper ACL机制中ProviderRegistry的设计缺陷
    搜索引擎项目的详细介绍
    SpinalHDL学习笔记(1)——Scala和sbt安装
    PostgreSQL 基于heap表引擎的事务 实现原理
    工业智能网关BL110应用之九: 主要接口
  • 原文地址:https://blog.csdn.net/kyrie_sakura/article/details/127839361