答案:1.
空类型的示例中不包含任何信息,本来求siezof应该是0,但是当我们声明改类型的实列是时候,它必须再内存中占有一定的空间,否则无法使用这些实例。
对于至少占用多少内存,这由编译器决定。
在Visual Studio中,每个空类型的实例都占用一字节的空间。
还是1。
调用构造函数和析构函数只需要知道函数的地址即可,而这些函数的地址只与类型相关,而与类型的示例无关,编译器也不会因为这两个函数而再示例内添加任何额外的信息。
C++编译器一旦发现一个类型中由虚函数,就会为该类型生成虚函数表,并在该类型的每一个示例中添加一个指向虚函数表的指针。
在32位的机器上,一个指针占4字节,因此求sizeof得到4;如果是在64位的机器,则一个字节占8字节的空间,因此求sizeof得到8。
给出以下代码,分析编译器运行的结果:
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;
}
在上述代码中,复制构造函数A(A other)传入的参数是A的一个实例。由于是传值参数,我们把形参复制到实参会调用复制构造函数。因此,如果允许复制构造函数传值,就会在复制构造函数内调用复制构造函数,就会形成永无休止的递归调用从而导致栈溢出。因此,C++的标准不允许复制构造函数传值参数,在 Visual Studio和 GCC中,都将编译出错。要解决这个问题,我们可以把构造函数修改为A(const A& other)
,也就是把传值参数改成常量引用。
题目:如下为类型CMyString 的声明,请为该类型添加赋值运算符函数。
class CMyString
{
public:
CMyString(char *pData = nullptr);
CMyString(const CMyString& str);
~CMyString(void);
private:
char* m_pData;
};
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;
}
在经典解法中,我们在分配内存之前先调用了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;
}
C++中类和结构体的区别:
默认的防控属性(访问控制)不同,struct是public的,而class是private的。
C中结构体和C++结构体区别: