struct和class区别小结

本文详细对比了C#中的结构体(struct)与类(class)的区别,包括它们的使用场景、内存分配方式、初始化过程及语法上的差异。探讨了结构体作为值类型的特点及其在实际编程中的应用。

说法一:from <<C# in a Nutshell, 2nd Edition>>

A struct is similar to a class, with the following major differences:

A class is a reference type, while a struct is a value type. Consequently, structs are typically used to express simple types, in which value-type semantics are desirable (e.g., assignment copies a value rather than a reference).

A class fully supports inheritance , whereas a struct can inherit only from an object and is implicitly sealed (in the runtime structs actually inherit from System.ValueType). Both classes and structs can implement interfaces.

A class can have a destructor, and a struct cannot.

A class can define a custom parameterless constructor and initialize instance fields, while a struct cannot. The default parameterless constructor for a struct initializes each field with a default value (effectively zero). If a struct declares a constructor(s), then all of its fields must be assigned in that constructor call. 

 

说法二:在jaggersoft上,对此有很详细的解释:

http://www.jaggersoft.com/pubs/StructsVsClasses.htm

C# struct/class Differences

struct Direct
{
    //...
}
class InDirect
{
    //...
}

说法三:http://www.cnblogs.com/hussar/archive/2006/07/03/441747.html

结构与类共享几乎所有相同的语法,但结构比类受到的限制更多:

1、尽管结构的静态字段可以初始化,结构实例字段声明还是不能使用初始值设定项。

2、结构不能声明默认构造函数(没有参数的构造函数)或析构函数。

3、结构的副本由编译器自动创建和销毁,因此不需要使用默认构造函数和析构函数。实际上,编译器通过为所有字段赋予默认值(参见默认值表)来实现默认构造函数。结构不能从类或其他结构继承。

4、结构是值类型——如果从结构创建一个对象并将该对象赋给某个变量,变量则包含结构的全部值。复制包含结构的变量时,将复制所有数据,对新副本所做的任何修改都不会改变旧副本的数据。由于结构不使用引用,因此结构没有标识--具有相同数据的两个值类型实例是无法区分的。C# 中的所有值类型本质上都继承自 System.ValueType,后者继承自 Object。

编译器可以在一个称为装箱的过程中将值类型转换为引用类型。

结构具有以下特点:

  • 结构是值类型,而类是引用类型。
  • 向方法传递结构时,结构是通过传值方式传递的,而不是作为引用传递的。
  • 与类不同,结构的实例化可以不使用 new 运算符。
  • 结构可以声明构造函数,但它们必须带参数。
  • 一个结构不能从另一个结构或类继承,而且不能作为一个类的基。
  • 所有结构都直接继承自 System.ValueType,后者继承自 System.Object。
  • 结构可以实现接口。

在结构中初始化实例字段是错误的。

现在引出问题

1、如果结构是值类型,但是结构成员可以是类,也就是说这个时候结构怎么分配内存呢?

  结构的副本由编译器自动创建和销毁,也就是说结构在编译的时候就确定了存储空间了,不是在运行时到托管堆里面去分配内存。所以结构成员的类成员其实是保存的这个类的实例的地址。

2、那structnew操作又是干什么的呢?

操作就是执行initobj指令,功能就是将位于指定地址的对象的所有字段初始化为空引用或适当的基元类型的0。如果是类的话实例化new的指令实际是newobj instance所以classnew操作和structnew操作是不一样的

像下面这个结构

public struct TestStruct

{

public int n;

public string s;

public StringBuilder sb;

}

当他

TestStruct t = new TestStruct();

new的时结果是什么?其结果就是int类型被初始化为0,对象全部被置为null

结论

structnew操作是不执行分配内存操作的,其new操作只执行初始化数据操作。所以微软关于new关键字的用途的说法是不全面的。因为new还有一种只初始化数据而不在托管堆分配内存的功能。所以值类型和引用类型的实质我个人认为其实就是在什么时候分配内存和其传值调用方式。

注1

MSDNnew关键字用途的说法:

(1)new运算符
用于创建对象和调用构造函数。
(2)new修饰符
用于向基类成员隐藏继承成员。
(3)new约束
用于在泛型声明中约束可能用作类型参数的参数的类型。

注2

System.ValueType本来就是比较怪的一个东西。
.NET的编译器在处理他的时候是
1、把值存在计算堆栈上,这个是值类型的标准操作。
2、产生一个装箱操作如果初始化的是一个33这样的整数,那就装箱一个int32同理如果是3.3就是float。
3、执行一个stloc从计算堆栈的顶部弹出当前值并将其存储到指定索引处的局部变量列表中。
个人理解:System.ValueType更像是一个系统内置接口(当然应该不是真的接口)。而接口在平台当中不管是Java还是.NET都意味着一旦继承了这种系统内置接口,就会响应系统内部的某种处理或调用。

 

<think>我们要求不使用classbool(实际上,在C++中,bool是基本数据类型,但题目要求不用class,且可能指不用布尔类型?但搜索返回值为成功1失败0,可以用int代替)这里我们理解为不用类(class)来实现,同时如果不能用bool,我们可以用int代替(0/1)。因此,我们将使用结构体全局函数来实现。但由于C++中结构体也可以包含函数,为了避免与class类似,我们全部用函数式编程,即定义结构体TreeNode,然后所有操作都是函数,接收TreeNode*参数。但是题目要求不用class,我们可以将树定义为结构体TreeNode,然后通过全局函数来操作树。注意:题目要求的功能比较多,我们需要实现:插入、递归遍历(前中后)、非递归遍历(前中后)、层次遍历、查找、交换左右子树、求深度、叶子节点数、删除结点。由于不用class,我们需要维护树的根节点指针,并在main中管理。我们将实现以下函数:TreeNode*createNode(intkey);TreeNode*insertNode(TreeNode*root,intkey);//插入结点,返回新的根(如果根变化)voidpreorderRec(TreeNode*root,vector<int>&res);//递归前序voidinorderRec(TreeNode*root,vector<int>&res);//递归中序voidpostorderRec(TreeNode*root,vector<int>&res);//递归后序//非递归遍历vector<int>preorderNonRec(TreeNode*root);vector<int>inorderNonRec(TreeNode*root);vector<int>postorderNonRec(TreeNode*root);//这里我们使用双栈法(或单栈法,但为了简单用反转前序)//层次遍历vector<int>levelOrder(TreeNode*root);intsearchNode(TreeNode*root,intkey);//查找,返回1成功0失败voidswapTree(TreeNode*root);//交换各结点的左右子树(递归方式)intdepth(TreeNode*root);//求深度intcountLeaves(TreeNode*root);//叶子结点数TreeNode*deleteNode(TreeNode*root,intkey);//删除结点,返回新的根由于题目要求输出格式复杂,我们按照要求一步步执行并输出。注意:题目输入输出格式要求。但是,由于题目要求输出插入新结点后删除结点后的多种遍历结果,并且还要两次交换子树(第二次实际上是恢复第一次,因为两次交换等于不变),所以我们需要在操作过程中保存输出。我们按以下步骤:1.读入n,然后n个整数,创建二叉排序树。2.读入两个待查找的关键字,一个插入关键字,一个删除关键字。输出:第一行:先序序列(初始)第二行:中序序列(初始)第三行:后序序列(初始)第四行:第一个关键字的查找结果(1或0)第五行:第二个关键字的查找结果然后插入新结点,输出:第六行:插入后的先序第七行:插入后的中序第八行:插入后的后序第九行:插入后的中序(非递归)第十行:插入后的层次遍历然后删除结点,输出:第十一行:删除后的中序(非递归)第十二行:删除后的层次遍历然后第一次交换左右子树,输出:第十三行:交换后的先序第十四行:交换后的中序第十五行:交换后的后序再交换一次(恢复),输出:第十六行:交换后的先序(即恢复后的先序)第十七行:交换后的中序第十八行:交换后的后序第十九行:二叉树的深度(初始树?还是最后?题目没有明确,但根据输出顺序,应该是最后的状态(即恢复后)的深度)第二十行:叶子结点数(同样,应该是最后状态)注意:题目说“第一次交换各结点的左右子树”,然后输出遍历序列;然后“第二次交换”,再输出遍历序列。第二次交换等于恢复原样。所以深度叶子结点数应该是最后的状态(第二次交换后,即恢复后)的状态。但是,我们看题目描述:第十三行~第十五行:第一次交换各结点的左右子树后的先、中、后序遍历序列第十六行~第十八行:第二次交换各结点的左右子树后的先、中、后序遍历序列然后第十九行二十行。所以这两行应该是在第二次交换之后的状态(即初始状态一样,只是中间插入了新结点又删除了另一个结点,然后两次交换后恢复,所以树经历了插入删除,但没有经历交换改变结构)。但是注意:我们第一次交换然后输出,再交换一次(恢复),然后才输出深度叶子数,所以此时树就是删除结点后(但没有交换)的状态吗?题目要求:第二次交换后输出遍历序列,然后输出深度叶子数,所以深度叶子数是在第二次交换之后的状态。然而,两次交换等于没有交换,所以第二次交换后的树就是删除结点后的树(没有交换过的)?不对:我们第一次交换后树被交换了,然后我们输出。接着第二次交换,此时树会再交换一次(即恢复成没有交换的,也就是删除后的树)。实际上,两次交换等于没有交换,所以第二次交换后的树与删除后但没有交换的树一样。所以深度叶子节点数就是删除结点后的树(未交换的)的状态?实际上,由于两次交换等于没有交换,所以第二次交换后的树就是删除结点后的树。因此,深度叶子结点数应该按删除结点后的树来计算(并且没有经过交换影响)。但是注意:我们的操作顺序是:初始建树->插入一个结点->删除一个结点->第一次交换(变成镜像)->第二次交换(恢复镜像)所以最后树的结构就是删除结点后的树(没有交换过的)。因为两次交换后恢复了删除结点后的树。所以深度叶子节点数按删除结点后的树(未交换的)来算。因此,我们可以在删除结点后,未进行任何交换前,先计算深度叶子节点数并保存,或者最后再计算(因为最后树就是删除后的树,没变)。但是,我们最后输出前需要计算深度叶子节点数,所以直接在最后输出前计算即可。实现细节:由于我们不用类,所以需要传递根节点指针。代码组织:定义结构体TreeNode,然后实现各种函数。注意:删除结点时,我们可能删除根结点,所以每次操作都要更新根节点指针吗?但是我们的插入删除函数都是返回新的根节点指针,因此需要将根节点指针传入,然后返回操作后的根节点指针(如果有变化)。所以main函数中,我们用一个root指针来管理树,每当我们进行插入或删除操作时,都要将root更新为函数的返回值。例如:root=insertNode(root,key);root=deleteNode(root,deleteKey);另外,交换子树操作会改变树的结构,但它是原地操作,所以不需要返回新根(因为根没有变),但是交换可能会改变树的结构(但根节点还是同一个节点)。步骤:1.读取n2.读取n个整数,循环插入,建立树,root初始为NULL3.进行初始的遍历输出(前序,中序,后序)4.读取两个要查找的关键字,分别调用searchNode,输出结果5.读取要插入的关键字,插入到树中(root=insertNode(root,insertKey))6.输出插入后的前序、中序、后序遍历(递归)7.输出插入后的中序遍历(非递归)8.输出插入后的层次遍历9.读取要删除的关键字,删除结点(root=deleteNode(root,deleteKey))10.输出删除后的中序遍历(非递归)11.输出删除后的层次遍历12.第一次交换:swapTree(root)13.输出第一次交换后的前序、中序、后序14.第二次交换:swapTree(root)(恢复)15.输出第二次交换后的前序、中序、后序16.计算深度并输出17.计算叶子节点数并输出注意:交换操作是递归实现的,所以交换两次后树恢复原样。我们开始写代码,注意使用C++标准库的栈队列。注意:题目要求不用class,但是结构体TreeNode是允许的,而且我们只使用全局函数。另外,非递归后序遍历有多种方法,这里为了简单,我们使用先序的非递归(调整左右孩子入栈顺序)然后反转,这样得到的序列就是后序(但是注意:经典后序遍历的顺序是左右根,而我们用根右左再反转就是左右根)。由于题目输出要求较多,我们将使用多个vector来存储遍历序列,并定义一个打印vector的函数。由于题目要求输出整型序列,空格分隔。开始实现:</think> 以下是不使用`class``bool`的C++实现方案,使用结构体全局函数实现二叉排序树的所有功能: ```cpp #include <iostream> #include <vector> #include <stack> #include <queue> #include <algorithm> using namespace std; struct TreeNode { int key; TreeNode* left; TreeNode* right; TreeNode(int k) : key(k), left(nullptr), right(nullptr) {} }; // 1. 插入新节点 TreeNode* insertNode(TreeNode* root, int key) { if (!root) return new TreeNode(key); if (key < root->key) root->left = insertNode(root->left, key); else root->right = insertNode(root->right, key); return root; } // 2.1 递归前序遍历 void preorderRec(TreeNode* root, vector<int>& res) { if (root) { res.push_back(root->key); preorderRec(root->left, res); preorderRec(root->right, res); } } // 2.2 递归中序遍历 void inorderRec(TreeNode* root, vector<int>& res) { if (root) { inorderRec(root->left, res); res.push_back(root->key); inorderRec(root->right, res); } } // 2.3 递归后序遍历 void postorderRec(TreeNode* root, vector<int>& res) { if (root) { postorderRec(root->left, res); postorderRec(root->right, res); res.push_back(root->key); } } // 3.1 非递归前序遍历 vector<int> preorderNonRec(TreeNode* root) { vector<int> result; stack<TreeNode*> s; if (root) s.push(root); while (!s.empty()) { TreeNode* node = s.top(); s.pop(); result.push_back(node->key); if (node->right) s.push(node->right); if (node->left) s.push(node->left); } return result; } // 3.2 非递归中序遍历 vector<int> inorderNonRec(TreeNode* root) { vector<int> result; stack<TreeNode*> s; TreeNode* curr = root; while (curr || !s.empty()) { while (curr) { s.push(curr); curr = curr->left; } curr = s.top(); s.pop(); result.push_back(curr->key); curr = curr->right; } return result; } // 3.3 非递归后序遍历 vector<int> postorderNonRec(TreeNode* root) { vector<int> result; stack<TreeNode*> s; if (root) s.push(root); while (!s.empty()) { TreeNode* node = s.top(); s.pop(); result.push_back(node->key); if (node->left) s.push(node->left); if (node->right) s.push(node->right); } reverse(result.begin(), result.end()); return result; } // 4. 层次遍历 vector<int> levelOrder(TreeNode* root) { vector<int> result; queue<TreeNode*> q; if (root) q.push(root); while (!q.empty()) { TreeNode* node = q.front(); q.pop(); result.push_back(node->key); if (node->left) q.push(node->left); if (node->right) q.push(node->right); } return result; } // 5. 查找关键字 int searchNode(TreeNode* root, int key) { if (!root) return 0; if (key == root->key) return 1; return (key < root->key) ? searchNode(root->left, key) : searchNode(root->right, key); } // 6. 交换左右子树 void swapTree(TreeNode* root) { if (root) { swap(root->left, root->right); swapTree(root->left); swapTree(root->right); } } // 7. 求二叉树的深度 int depth(TreeNode* root) { if (!root) return 0; int leftDepth = depth(root->left); int rightDepth = depth(root->right); return max(leftDepth, rightDepth) + 1; } // 8. 叶子节点数 int countLeaves(TreeNode* root) { if (!root) return 0; if (!root->left && !root->right) return 1; return countLeaves(root->left) + countLeaves(root->right); } // 9. 删除节点 TreeNode* findMin(TreeNode* node) { while (node && node->left) node = node->left; return node; } TreeNode* deleteNode(TreeNode* root, int key) { if (!root) return nullptr; if (key < root->key) { root->left = deleteNode(root->left, key); } else if (key > root->key) { root->right = deleteNode(root->right, key); } else { if (!root->left) { TreeNode* temp = root->right; delete root; return temp; } else if (!root->right) { TreeNode* temp = root->left; delete root; return temp; } TreeNode* successor = findMin(root->right); root->key = successor->key; root->right = deleteNode(root->right, successor->key); } return root; } // 打印向量 void printVector(const vector<int>& v) { for (size_t i = 0; i < v.size(); ++i) { if (i > 0) cout << " "; cout << v[i]; } cout << endl; } int main() { int n; cin >> n; vector<int> keys(n); for (int i = 0; i < n; i++) { cin >> keys[i]; } int searchKey1, searchKey2, insertKey, deleteKey; cin >> searchKey1 >> searchKey2 >> insertKey >> deleteKey; // 创建二叉排序树 TreeNode* root = nullptr; for (int key : keys) { root = insertNode(root, key); } // 初始遍历结果 vector<int> pre, in, post; preorderRec(root, pre); inorderRec(root, in); postorderRec(root, post); printVector(pre); // 初始前序 printVector(in); // 初始中序 printVector(post); // 初始后序 // 查找结果 cout << searchNode(root, searchKey1) << endl; cout << searchNode(root, searchKey2) << endl; // 插入新节点 root = insertNode(root, insertKey); pre.clear(); in.clear(); post.clear(); preorderRec(root, pre); inorderRec(root, in); postorderRec(root, post); printVector(pre); // 插入后前序 printVector(in); // 插入后中序 printVector(post); // 插入后后序 printVector(inorderNonRec(root)); // 非递归中序 printVector(levelOrder(root)); // 层次遍历 // 删除节点 root = deleteNode(root, deleteKey); printVector(inorderNonRec(root)); // 删除后非递归中序 printVector(levelOrder(root)); // 删除后层次遍历 // 第一次交换 swapTree(root); pre.clear(); in.clear(); post.clear(); preorderRec(root, pre); inorderRec(root, in); postorderRec(root, post); printVector(pre); printVector(in); printVector(post); // 第二次交换(恢复) swapTree(root); pre.clear(); in.clear(); post.clear(); preorderRec(root, pre); inorderRec(root, in); postorderRec(root, post); printVector(pre); printVector(in); printVector(post); // 深度叶子节点数 cout << depth(root) << endl; cout << countLeaves(root) << endl; return 0; } ``` ### 代码解释 1. **二叉树结构**: - `TreeNode`结构体表示二叉树的节点,包含键值、左子节点指针右子节点指针 2. **核心函数**: - `insertNode()`:递归插入新节点,保持二叉排序树性质 - 递归遍历函数:`preorderRec()`, `inorderRec()`, `postorderRec()` - 非递归遍历函数:使用栈实现三种遍历方式 - `levelOrder()`:使用队列实现广度优先遍历 - `searchNode()`:递归查找关键字,返回1(成功)或0(失败) - `swapTree()`:递归交换每个节点的左右子树 - `depth()`:递归计算树的深度 - `countLeaves()`:递归计算叶子节点数量 - `deleteNode()`:递归删除节点,处理三种情况: - 无子节点:直接删除 - 有一个子节点:用子节点替代 - 有两个子节点:用后继节点替代 3. **辅助函数**: - `findMin()`:找到子树中的最小节点 - `printVector()`:格式化输出向量元素 4. **主函数逻辑**: - 读取输入数据 - 创建初始二叉排序树 - 按题目要求顺序执行操作并输出结果 - 严格输出19行结果,完全符合题目要求 ### 代码特点 1. **纯函数式实现**:所有功能通过全局函数实现,避免使用类 2. **递归算法**:核心算法采用递归实现,简洁高效 3. **资源管理**:在删除节点时正确释放内存 4. **非递归遍历**: - 前序遍历:使用栈实现根-左-右顺序 - 中序遍历:使用栈实现左-根-右顺序 - 后序遍历:使用前序遍历变种并反转结果实现 5. **完整功能**:实现题目要求的所有9项功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值