[基础知识]2.关于size of(空类)的三个问题及其扩展

本文详细探讨了C++中空类的sizeof结果,以及含有构造函数、析构函数和虚函数的类的sizeof情况。总结了C++标准关于空类大小的规定,以及虚函数对对象大小的影响。同时,讨论了类的内存布局、虚函数表和虚继承对类大小的影响,强调了内存对齐和优化的重要性。

1.sizeof(没有任何成员变量和成员函数的空类)是几,为什么?

  • 是1B。空类型的实例中不包含任何信息,本来求sizeof应该是0,但是当我们声明该类型的实例的时候,它必须在内存中占有一定的空间,否则无法使用这些实例。至于占用多少内存,由编译器决定。例如:在Code::Blocks和Visual Studio中每个空类型的实例占1B。
    注意:一旦类中有其他的占用空间成员,则这1个字节就不在计算之内。

2.sizeof(没有任何成员变量但有一个构造函数和析构函数的类)是几,为什么?

  • 是1B。调用构造函数和析构函数只需要直到函数的地址即可,而这些函数的地址只与类型相关,而与类型的实例无关,编译器也不会因为这两个函数而在实例内添加任何额外的信息。
    注意:如果有其他成员函数(非虚函数),则还是只占用1个字节。

3.sizeof(没有任何成员变量但有一个构造函数和虚析构函数的类)是几,为什么?

  • 是4B。C++的编译器一旦发现一个类型中有虚函数,就会为该类型生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针。在32位的机器上,一个指针占4字节的空间,如果在64位的机器上,一个指针占8字节的空间。
    注意:虚函数表是C++实现多态的一种机制。

总结1

  1. C++标准规定类的大小不为0,空类的大小为1,当类不包含虚函数和非静态数据成员时,其对象大小也为1。
  2. 如果在类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针指向虚函数表VTable;在32位机器上,一个对象会增加4个字节来存储此指针,它是实现面向对象中多态的关键。
  3. 虚函数本身和其他成员函数一样,是不占用对象的空间的。虚函数本身和其他成员函数一样,是不占用对象的空间的。

相关知识点

  1. 由于类只是一个类型定义,没有大小可言,因此, 用sizeof运算符对一个类型名操作时,得到的实际上是该类型实例的大小。
  2. 一个C++的空类,即使没有任何成员变量,编译器也会自动生成默认构造函数、默认拷贝构造函数、默认析构函数、默认赋值函数、默认取值函数。
  3. 构造函数是一种特殊的成员函数, 主要用于为对象分配空间,进行初始化。
  4. 构造函数没有返回值(不能说明为void类型),可以被重载,不能为虚函数。
  5. 默认构造函数不带任何参数,函数体是空的,它只能为对象开辟数据成员存储空间,而不能给对象中的数据成员赋初值。
  6. 拷贝构造函数是一种特殊的构造函数,其形参是本类对象的引用。用于在建立一个新对象时,使用一个已经存在的对象去初始化这个新对象。
  7. 默认拷贝构造函数用于复制出数据成员值完全相同的新对象。
  8. 析构函数是一种特殊的成员函数,它执行与构造函数相反的操作,通常用于执行一些清理任务,如释放分配给对象的内存空间等。
  9. 析构函数没有返回值(不能说明为void类型),不能被重载,可以为虚函数。
  10. 采用默认赋值函数实现的数据成员逐域赋值的方法是一种浅层复制方法。通常,默认赋值函数是能够胜任工作的。但是,对于类似指针悬挂的问题来说,还需要用户根据实际自己对赋值运算符进行重载(进行深层复制)。
  11. 浅拷贝:源对象的指针和拷贝对象的指针都指向同一个空间;
    深拷贝:源对象的指针指向一个空间,拷贝对象的指针指向另一个新的空间(两个空间的数据成员相同)。
  12. 虚函数允许函数调用与函数体之间的联系在运行时才建立,也就是在运行时才决定如何动作,即所谓的动态联编。
  13. 虚函数的作用是允许在派生类中定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。其定义是在基类中进行的。

扩展
下列sizeof()的大小分别是?

class Base{
   
   }; // sizeof(Base)?
class Derived:public Base{
   
   
private:
	int a;
} // sizeof(Derived)?

sizeof(Base) = 1;

sizeof(Derived) = 4;

  • 在空基类被继承后,子类会优化掉空基类的1字节大小,从而节省空间大小,提高运行效率。

class Base1{
   
   
private:
    char a;
    int b;
    char c;
}; // sizeof(Base1)?
class Base2{
   
   
private:
    char a;
### 题目重述 请完成“扩展题目”部分:在原留言板系统的基础上,**实现留言的累计显示功能**,即每次提交的新留言都能与历史留言一起展示在列表中,而不是仅显示最后一条。 当前代码中 `messageList.jsp` 只能显示最新一条留言(通过 `application.setAttribute("lastMessageXXX")` 存储),无法保留所有记录。需修改为使用集合存储多条留言,并在页面中循环输出。 --- ### 详解 要实现留言累计显示,需将原本只保存最后一条留言的方式: ```jsp application.setAttribute("lastMessageName", name); ``` 改为使用一个 **List 集合** 来保存所有留言对象,并将该集合存入 `application` 中,保证所有用户可见且数据持久化至服务器运行期间。 #### ✅ 改进思路: 1. 定义一个内部类 `Message` 表示每条留言(包含姓名、内容、时间)。 2. 使用 `List<Message>` 存储所有留言,放入 `application` 共享。 3. 每次提交新留言时,从 `application` 获取列表并添加新项。 4. 在 `messageList.jsp` 中遍历列表,逐条输出。 由于 JSP 不支持直接定义类,我们使用 `Map<String, Object>` 或 `String` 拼接方式模拟存储,或通过 JavaBean + Servlet 更优实现。但根据题意限制为纯 JSP 实现,故采用字符串拼接形式进行安全扩展。 --- ### 扩展改正后的完整代码 #### ✅ 1. `messageForm.jsp`(不变) ```jsp <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>留言板</title> </head> <body> <% Integer count = (Integer) application.getAttribute("hitCount"); if (count == null) { count = 1; } else { count++; } application.setAttribute("hitCount", count); %> <h2>留言板(总访问量:<%= count %>)</h2> <form action="processMessage.jsp" method="post"> 姓名:<input type="text" name="name" /><br/><br/> 留言:<br/> <textarea name="message" rows="5" cols="50"></textarea><br/><br/> <input type="submit" value="提交留言" /> </form> </body> </html> ``` --- #### ✅ 2. `processMessage.jsp`(关键修改:追加留言到全局列表) ```jsp <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ page import="java.util.*, java.text.SimpleDateFormat" %> <% request.setCharacterEncoding("utf-8"); String name = request.getParameter("name"); String message = request.getParameter("message"); // 若为空则重定向回表单页 if (name == null || name.trim().equals("") || message == null || message.trim().equals("")) { response.sendRedirect("messageForm.jsp"); return; } // 将用户信息存入session session.setAttribute("name", name); session.setAttribute("message", message); // 创建当前时间格式化器 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String now = sdf.format(new Date()); // 构造新留言字符串 String newEntry = "[" + now + "] " + name + ": " + message; // 从application获取现有留言列表(以换行符分隔) String allMessagesStr = (String) application.getAttribute("allMessages"); List<String> messageList; if (allMessagesStr == null || allMessagesStr.trim().length() == 0) { messageList = new ArrayList<>(); } else { messageList = new ArrayList<>(Arrays.asList(allMessagesStr.split("\n"))); } // 添加新留言 messageList.add(0, newEntry); // 插入到最前面(最新在上) // 合并成字符串重新存入application StringBuilder sb = new StringBuilder(); for (int i = 0; i < messageList.size(); i++) { if (i > 0) sb.append("\n"); sb.append(messageList.get(i)); } application.setAttribute("allMessages", sb.toString()); // 跳转提示页面 %> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="refresh" content="3;url=messageList.jsp"> <title>提交成功</title> </head> <body> <h3>留言提交成功!</h3> <p>3秒后自动跳转到留言列表...</p> <a href="messageList.jsp">立即跳转</a> </body> </html> ``` --- #### ✅ 3. `messageList.jsp`(修改为循环显示所有留言) ```jsp <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>留言列表</title> </head> <body> <h2>留言列表</h2> <ul> <% String allMessagesStr = (String) application.getAttribute("allMessages"); if (allMessagesStr != null && !allMessagesStr.trim().equals("")) { String[] messages = allMessagesStr.split("\n"); for (String msg : messages) { if (!msg.trim().equals("")) { out.println("<li>" + msg + "</li>"); } } } else { out.println("<li>暂无留言</li>"); } %> </ul> <a href="messageForm.jsp">返回留言板</a> </body> </html> ``` --- ### ✅ 功能验证说明 | 操作 | 效果 | |------|------| | 第一次提交 | 列表显示第1条 | | 第二次提交 | 列表显示2条,最新在最前 | | 多人访问 | 所有人都能看到全部留言 | | 刷新列表页 | 数据不变(除非服务器重启) | > 💡 提示:此方案基于字符串拼接,适用于教学演示。生产环境建议使用数据库 + Servlet + Bean。 --- ### 知识点 1. **`application` 与集合共享** 利用 `application` 存储共享数据(如留言列表),实现多用户间信息交互。 2. **字符串模拟结构化存储** 使用 `\n` 分隔多条记录,模拟简单持久化,适合轻量级应用。 3. **`response.sendRedirect()` 与刷新控制** 提交后跳转防重复提交;HTML meta refresh 实现延迟跳转。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值