C#

[StructLayout(LayoutKind.Sequential)] 是什么意思??
结构体是由若干成员组成的.布局有两种
1.Sequential,顺序布局,比如
struct S1
{int a;int b;
}
那么默认情况下在内存里是先排a,再排b
也就是如果能取到a的地址,和b的地址,则相差一个int类型的长度,4字节
[StructLayout(LayoutKind.Sequential)]
struct S1
{int a;int b;
}
这样和上一个是一样的.因为默认的内存排列就是Sequential,也就是按成员的先后顺序排列.
2.Explicit,精确布局
需要用FieldOffset()设置每个成员的位置
这样就可以实现类似c的公用体的功能
[StructLayout(LayoutKind.Explicit)]
struct S1
{[FieldOffset(0)]int a;[FieldOffset(0)]int b;
}
这样a和b在内存中地址相同

C#与C++在语言层面的区别(不断补充)

注:C#语言发展十分迅速,而且仍然有很大的提升空间,所以现在写下的有关C#语言上的一些限制,可能过一两年就不同了,所以需要不断更新。至于C++,因为已经很久没怎么变动,所以就容易得多。

(*) 允许初始化成员变量
C#允许
C++不允许

(*) 编译器自动添加默认无参构造函数
c++:当用户实现了有参构造函数,编译器就不添加了。
c#:对于class与C++一样。但对于struct,无论用户实不实现有参构造函数,编译器都会无条件地保留一个无参构造函数。并且,不允许程序员编写自己的无参构造函数。(编译器保留的无参构造函数的行为就是分配内存,然后把所有值类型字段置0,引用类型字段置null)

(*) char和long的长度不同
C#: char 2byte, long 8byte, ulong 8byte
C++: char 1byte, long 4byte, ulong 4byte

(*) struct的内存分配不同
C#: 在栈上分配
c++: 在哪分配由程序员说了算,只有用new的时候才在堆上分配

(*) 默认参数值
C++: 函数允许有默认参数值,且必须放在参数列表的最后
C#: 不支持默认参数值,可以用函数重载来实现同名函数的多种signature

(*) 条件编译

C#里的#if比较全才,能代替C++中的#if, #ifdef, #ifndef

C++:#ifdef DEBUG,  #ifndef DEBUG

C#:#if DEBUG, #if !DEBUG

(*) 设置按几字节对齐(pack)
C++:

#pragma pack(1) // modify 
struct A
{
 int i;
 char c;
};
#pragma pack(4) // reset default

在C++里,一旦修改了pack后,就一直生效,直到再把它改回去。

C#:

[StructLayout(LayoutKind.Sequential, Pack=1)]    
struct A
{        
 int i;
 char c;
}

在c#里pack是作为struct的属性,所以不像c++那样一旦指定后面都生效,而是要为每个struct分别指定。

(*) sizeof
C++: 不用多说了
C#: 
在C#里,sizeof只能查看几种基元值类型,如int, double等等,而且只能跟类型名,不能跟变量。简直废物一个。
取而代之的是System.Runtime.InteropServices.Marshal.SizeOf,但也有个限制,就是只能查看值类型,查看所有引用类型(如string)都会使程序崩溃。

(*) 多维数组
拿动态分配的二维数组举例,C++和c#都能实现jagged矩阵(内存不连续)和整齐的矩阵(内存连续)。
先看两种语言各自实现jagged和连续内存二维数组的代码。
C++

int** jagged = new int*[3];

for(int i = 0; i < 3; ++i)
    jagged[i] = new int[4];

for(int i = 0; i < 3; ++i)    
    for(int j = 0; j < 4; ++j)        
        jagged[i][j] = i * 4 + j;

int *matrix = new int[3 * 4];
for(int i = 0; i < 3 * 4; ++i)
    matrix[i] = i;

C#

int[][] jagged = new int[3][];
for (int i = 0; i < jagged.Length; i++)
{
    jagged[i] = new int[4];
}
for (int i = 0; i < jagged.Length; i++)
{
    for (int j = 0; j < jagged[i].Length; j++)
    {
        jagged[i][j] = i * 4 + j;
    }
}

int[,] matrix = new int[3, 4];
for (int i = 0; i < matrix.Length; i++)
{
    matrix[i/4, i%4] = i;
    // matrix[i] = i;   Error in C# but OK in C++
}

结果比较令人惊讶,在C#教材中不断强调的jagged其实在易用度上与C++几乎没有区别。反倒是连续内存的二维数组,C#要比C++易用,C#可以用matrix[row, low]来存取元素,而C++不能。因为对于连续内存的多维数组(即用[,,]形式定义的)C#有Rank的概念,而C++没有。例如上面C#代码中matrix.Rank==2。但值得注意的是,jagged.Rank==1,不是2。

(*) namespace
关于namespace其实比较杂,有些不怎么用得到,等用到了再来补充:)
最常用到的就是:当命名出现冲突时,改成长一点的更加详尽的名字就可以了,无论C#还是C++
C++
定义别名: namespace fbz = ::foo::bar::baz;
支持无名namespace,用以把作用域限制在本文件,类似static在限制作用域方面的作用

C#
定义别名: using Fbz = Foo.Bar.Baz;
namespace嵌套:

//可以这样:
namespace OuterNamespace
{
    namespace InnerNamespace
    {
        class A
        {

}
    }
}
//也可以这样(我更喜欢这样,因为这可以省掉很多缩进):
namespace OuterNamespace.InnerNamespace
{
    class B
    {

}
}

(*) 构造函数初始化列表

C++:既可以初始化该类本身,也可以初始化其基类的成员。
在C++中,初始化列表是必不可少的,因为以下两种情况下,它是必需的:
1) const成员的初始化
2) 基类没有默认构造函数(因为在创建子类之前必须先创建基类,而基类因为没有默认构造函数,需要显示调用构造函数)

c#:只可以初始化其基类的成员
对于上面提到的两种情况在C#中有所不同:
1) C#的const变量是静态的,必须在编译时确定值。在功能上与C++中的const相同的是readonly,但readonly在写法上允许member = value。
2) 在C#中不存在“没有默认构造函”的情况,因为编译器总会给自动加上。但如果把默认构造函数显示声明为private,就无法调用了。那么就需要用base关键字:
public SomeConstructor(int a, int b, int c) : base(a, b)
{
     ...
}

除了上面说的基类默认构造函数是private的情况,使用base(...)初始化父类成员也会提高效率,因为如果不使用,就会调用父类默认的构造函数,父类先被构造一遍,然后在子类的构造函数内部再给父类的成员赋值,可不就多余了。

(*) 多态的enable与disable

enable多态
c++:若父类方法有virtual,子类默认是override(不过C++没有override关键字,只有virtual)
c#:若父类方法有virtual,子类默认是覆盖(即new),好在会给你警告,提示你如果想多态请加上override关键字

disable多态(子类向父类隐藏实现,有点像孩子瞒着家长)

C++:无法实现

C# :用new关键字来取代override

(*) 抽象类与抽象(纯虚)函数
无论纯虚还是抽象都是一个目的,即强制继承者实现。首先统一一下术语,以下用abstract来表示纯虚和抽象。
abstract函数只能出现在abstract类里,c++和C#都是这样做的,只不过实现的方式略有不同:
C++:只要一个类包含了abstract函数,其所在的类就自动变成abstract。
C#:如果一个类包含了abstract函数,其所在的类必须显示声明为abstract。
c++例子:

class A
{
public:    
    virtual void foo()=0;
    void bar()
    {
        cout << "A::bar" << endl;
    }
};

class B : public A
{
public:
    void foo()
    {
        cout << "B::foo" << endl;
    }
};
int main()
{                    
    B b;
    b.foo();
    b.bar();
    return 0;
}

C#例子:

namespace ConsoleApplication1
{
    abstract class A
    {
        abstract public void foo();
        public void bar() 
        {
            Console.WriteLine("A.bar");
        }
    }

class B : A
    {
        override public void foo() // override不能少,否则编译器会认为B没有实现A中的foo函数而报错
        {
            Console.WriteLine("B.foo");
        }
    }

class Program
    {
        static void Main(string[] args)
        {
            B obj = new B();
            obj.foo();
            obj.bar();
        }
    }
}

(*) 类型转换
c++
除了兼容C的不安全的强制类型转换,它还有其他四种转换:
const_cast<T>(expr)        去除const性
dynamic_cast<T>(expr)      用于父类指针转换成子类指针(子类指针 = dynamic_cast<子类指针>(父类指针);),如果类型不符则返回NULL
reinterpret_cast<T>(expr)  类似C的强转,据说是不应该出现的,毁掉程序可移植性的,不存在讨论价值的转换。
static_cast<T>(expr)       用于取代C的强转。值得注意的是,static_cast可将non-const转成const,即赋予const性,但去除const性则只能用const_cast。

C#
C#的强制类型转换虽然写法上和C的一样,但加了很多安全限制。有的情况下编译不过,有的情况下运行时抛出异常。
除了用括号,还有两个与转型相关的关键字:is 和 as
is举例:
if(tiger is Animal)
as举例:
Tiger tiger = animal as Tiger;
如果animal不是一个Tiger实体,则tiger被赋值为null.

(*)全局变量
即存在于任何一个类之外的变量
c++允许有
C#不允许有

(*) 异常
C#和C++中的异常处理仅仅通过类型而不是通过值来匹配的,否则又回到了传统的错误处理技术上去了,所以catch块的参数可以没有参数名称,只需要参数类型,除非要使用那个参数。

C++
1)捕捉所有异常是:
catch(...)
2)支持exception specifications,即可以指出函数可能抛出异常的类型:
例如:void function(int x) throw() //意思是函数实现者保证此函数不会抛出任何异常
3)没有finally,除非用SEH

C#异常的补充:
1.虽然异常对象看上去像局部对象,但并非创建在函数栈上,而是创建在专用的异常栈上,因此它才可以跨接多个函数而传递到上层,否则在栈清空的过程中就会被销毁。 
2.函数原型中的异常说明要与实现中的异常说明一致,否则容易引起异常冲突。由于异常处理机制是在运行时有异常时才发挥作用的,因此如果函数的实现中抛出了没有在其异常说明列表中列出的异常,则编译器并不能检查出来。但是当运行时如果真的抛出了这样的异常,就会导致异常冲突。因为你没有提示函数的调用者:该函数会抛出一种没有被说明的即不期望的异常,于是异常处理机制就会检测到这个冲突并调用标准库函数unexcepted(),unexcepted()的默认行为就是调用terminate()来结束程序。实际工作中使用set_unexcepter()来预设一个回调函数。 
3.Windows的SEH也是很好的异常处理方式。而且写C++程序的最高境界是尽量少用try,catch,所以……
4.catch块的参数应采用常量引用传递而不是值传递,不仅可以提高效率,还可以利用对象的多态性。如果用值传递则可能出现子类向父类转换时出现的截断现象。

C#
1)捕捉所有异常是:(写法比C++更简单)

catch
{
 ...
}


catch(Exception)
2)不支持exception specifications
例如:void function(int x) throw() //意思是函数实现者保证此函数不会抛出任何异常
3)有finally,而且不写catch照样可以有finally。甚至using block展开成IL后就是try{}finally{}的结构。

C#异常的补充:
1.Exception类的成员非常多,其中两个我认为比较有用,一个是StackTrace属性。另一个是Data属性(本质上是一个IDictionary),利用Data属性可以任意添加自定义的新成员,例如ex.Data.Add("TimeStamp", String.Format("{0}", DateTime.Now));

2.自定义异常的最佳实践

一般的应用程序异常应该继承自ApplicationException,而不是SystemException。这样可以很好地区分哪些异常是应用程序产生的,哪些是.NET底层类库产生的。所以一般简单的自定义的异常都这么写:
public class EngineIsDeadException : ApplicationException
{
 public EngineIsDeadException() { }
 public EngineIsDeadException(string message) : base(message) { }
}
.NET最佳实践的自定义异常比较复杂,当你在VS里写下Except+Tab产生出代码片段就知道了。

(*) 回调
C++:和C一样使用函数指针,静态方法还算方便,实例的方法比较麻烦。下面是实例方法指针的sample code:

class A
{
public:    
    void foo()
    {
        cout << "foo" << endl;
    }
    void bar()
    {
        cout << "bar" << endl;
    }
};

typedef void (A::*f)(void);

void func(f fp)
{
    A a;
    (a.*fp)();
}

int main()
{    
    f fp = &A::foo;
    func(fp);

fp = &A::bar;
    func(fp);
    
    return 0;
}

从上面代码可以看出,在C++里实例的函数指针必须加上实例名做前缀。虽然号称是指向实例成员的函数指针,但也仅仅是个指针而已(vtbl中的偏移量),它对实例的状态一无所知。而C#就不同了。

C#:使用委托:对实例方法支持得更好,支持异步。

举一个C#中实例函数的委托的例子:

class MyClass
    {
        private string name;

public MyClass(string name)
        {
            this.name = name;
        }

public void DisplayName() 
        {
            Console.WriteLine("{0}", name);
        }
    }

class Program
    {
        // 委托其实就相当于一个类型。这里,类型的名字叫SimpleDelegate
        public delegate void SimpleDelegate();

static void Main()
        {
            MyClass a = new MyClass("A");
            MyClass b = new MyClass("B");
            
            // 用实例方法DisplayName初始化
            SimpleDelegate d = new SimpleDelegate(a.DisplayName);            
            d();

d = new SimpleDelegate(b.DisplayName);
            d();
        }
    }

(*) 数组的创建

C++无论在堆上还是栈上创建数组,都是实实在在地分配内存,并对数组中的每一个元素调用构造函数。

C#则不一样。对值类型它分配内存并初始化成0,对引用类型则只给每个对象分配一个引用,并初始化成null。

MyClass[] classArray = new MyClass[1]; // No instance is created, classArray[0] is null
MyStruct[] structArray = new MyStruct[1]; // struct is created indeed, structArray[0] is an existing struct
出处;http://www.cnblogs.com/dc10101/archive/2009/03/09/1407266.html

C#与C++在语言层面的区别(不断补充)相关推荐

  1. c语言c 的区别,c语言和c 具体有哪些区别?

    首先,C++几乎是兼容C语言的,然后再来谈区别,总结起来就是一下几点. 1.面向对象 C++在语言层面上是遵循面向对象的基本要素的,包括封装,继承,多态. C++支持一种称为函数对象的对象,即重载对象 ...

  2. c与python的区别-Python与C语言有什么区别?

    答题练手.手机排版不佳请谅解~ 更新2:还是关于编译和解释 二者的本质区别是在编译/解释器的总体工作方式上的,编译器是off-line,解释器是on-line.编译器把整个程序读进来,进行一系列变大变 ...

  3. php和python区别-PHP与Python语言有哪些区别之处?选择哪一个好?

    其实针对不同阶段的学者来说,他们的需求自然也有所不同.本篇文章主要分析了php和python哪个好以及介绍PHP与Python的区别之处,希望对还在为学习PHP和Python中哪一个语言而摇摆不定的朋 ...

  4. python和c语言有什么关系-Python与C语言有什么区别?

    答题练手.手机排版不佳请谅解~ 更新2:还是关于编译和解释 二者的本质区别是在编译/解释器的总体工作方式上的,编译器是off-line,解释器是on-line.编译器把整个程序读进来,进行一系列变大变 ...

  5. python是c语言写的吗-Python与C语言有什么区别?

    答题练手.手机排版不佳请谅解~ 更新2:还是关于编译和解释 二者的本质区别是在编译/解释器的总体工作方式上的,编译器是off-line,解释器是on-line.编译器把整个程序读进来,进行一系列变大变 ...

  6. 数据操作语言DML及数据定义语言DDL的区别

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 数据操作 ...

  7. c++和java哪个好学_【技术科普】C语言和java语言有些什么区别?

    C语言和java语言有些什么区别? 可以打个比方吧: C语言就像一个初创公司的老板,由于人少经费少,从技术.财务.市场等都需要自己管,经常把自己累的跟狗一样: Java语言就像一个上市大公司的老板,人 ...

  8. python和c 的区别-Python与C语言有什么区别?

    答题练手.手机排版不佳请谅解~ 更新2:还是关于编译和解释 二者的本质区别是在编译/解释器的总体工作方式上的,编译器是off-line,解释器是on-line.编译器把整个程序读进来,进行一系列变大变 ...

  9. php和python-PHP与Python语言有哪些区别之处?选择哪一个好?

    其实针对不同阶段的学者来说,他们的需求自然也有所不同.本篇文章主要分析了php和python哪个好以及介绍PHP与Python的区别之处,希望对还在为学习PHP和Python中哪一个语言而摇摆不定的朋 ...

最新文章

  1. CCS6.2超详细使用方法
  2. 计算机设备记录人耳感知不到的声音,现代教育技术考试复习资料新
  3. Java中的Volatile如何工作? Java中的volatile关键字示例
  4. _thread_in_vm_Java Thread类的静态void sleep(long time_in_ms,int time_in_ns)方法,带示例
  5. C++学习之路 | PTA乙级—— 1039 到底买不买 (20 分)(精简)
  6. iOS 自定义控件 progressView(环形进度条)
  7. shell export 作用
  8. 【Node】—nodemon的简单使用
  9. 今天看到了和我男朋友不一样的程序员!好帅哦!
  10. 在Java eclipse 中Spring Boot工具安装步骤
  11. Spring源码解析一 (IOC容器初始化深度解析)
  12. 在大学里我们应该学习什么
  13. PreScan快速入门到精通第三十二讲基于PreScan进行毫米波雷达传感器仿真
  14. 飞腾2000+按通道分配内存
  15. 共轭方式怎么判断_怎么判断共轭效应是吸电子共轭效应还是给电子共轭效应?吸电子基和给电子基是根据什么判断的?...
  16. 上蔡一高2021高考成绩查询,上蔡一高高考录取名单1
  17. 计网homework
  18. Ubuntu下UnixC的第二天
  19. 我的2021年度书单(主要教你面试怎么装B)
  20. linux 禁用IPv 6

热门文章

  1. 【Oracle学习笔记-4】内连接和外连接的区别
  2. C# winform打开Excel文档的方法总结!
  3. JAVA读取Properties文件对象常用方法总结
  4. LeetCode简单题目(#27 #28 #35 #38)-2019.10.23-4道
  5. telnet收发邮件
  6. HTML网页中table居中和表格内容居中
  7. 2019-12-17 TCP报头结构
  8. android shape 绘制气泡图,气泡图-自定义 shape
  9. jquery中ajax请求分类
  10. 北理工计算机专业汇编教材,李元章_北京理工大学计算机学院