程序员面试题精选100题(56)-C/C++/C#面试题(4)

问题(16:运行如下的C++代码,输出是什么?

class A

{

public:

    virtual void Fun(int number = 10)

    {

        std::cout << “A::Fun with number “ << number;

    }

};

 

class B: public A

{

public:

    virtual void Fun(int number = 20)

    {

        std::cout << “B::Fun with number “ << number;

    }

};

 

int main()

{

    B b;

    A &a = b;

    a.Fun();

}

答案输出B::Fun with number 10。由于a是一个指向B实例的引用,因此在运行的时候会调用B::Fun。但缺省参数是在编译期决定的。在编译的时候,编译器只知道a是一个类型a的引用,具体指向什么类型在编译期是不能确定的,因此会按照A::Fun的声明把缺省参数number设为10

            这一题的关键在于理解确定缺省参数的值是在编译的时候,但确定引用、指针的虚函数调用哪个类型的函数是在运行的时候。

问题(17:运行如下的C代码,输出是什么?

char* GetString1()

{

    char p[] = “Hello World”;

    return p;

}

 

char* GetString2()

{

    char *p = “Hello World”;

    return p;

}

 

 

int _tmain(int argc, _TCHAR* argv[])

{

    printf(“GetString1 returns: %s. \n”, GetString1());

    printf(“GetString2 returns: %s. \n”, GetString2());

 

    return 0;

}

答案输出两行,第一行GetString1 returns: 后面跟的是一串随机的内容,而第二行GetString2 returns: Hello World. 两个函数的区别在于GetString1中是一个数组,而GetString2中是一个指针

当运行到GetString1时,p是一个数组,会开辟一块内存,并拷贝“Hello World”初始化该数组。接着返回数组的首地址并退出该函数。由于pGetString1内的一个局部变量,当运行到这个函数外面的时候,这个数组的内存会被释放掉。因此在_tmain函数里再去访问这个数组的内容时,结果是随机的

当运行到GetString2时,p是一个指针,它指向的是字符串常量区的一个常量字符串。该常量字符串是一个全局的,并不会因为退出函数GetString2而被释放掉。因此在_tmain中仍然根据GetString2返回的地址得到字符串“Hello World”

问题(18运行下图中C#代码,输出的结果是什么

namespace StaticVariableInAppDomain

{

    [Serializable]

    internal class A : MarshalByRefObject

    {

        public static int Number;

 

        public void SetNumber(int value)

        {

            Number = value;

        }

    }

 

    [Serializable]

    internal class B

    {

        public static int Number;

 

        public void SetNumber(int value)

        {

            Number = value;

        }

    }

 

    class Program

    {

        static void Main(string[] args)

        {

            String assamblyName = Assembly.GetEntryAssembly().FullName;

            AppDomain domain = AppDomain.CreateDomain(“NewDomain”);

 

            A.Number = 10;

            String nameOfA = typeof(A).FullName;

            A a = domain.CreateInstanceAndUnwrap(assamblyName, nameOfA) as A;

            a.SetNumber(20);

            Console.WriteLine(“Number in class A is {0}”, A.Number);

 

            B.Number = 10;

            String nameOfB = typeof(B).FullName;

            B b = domain.CreateInstanceAndUnwrap(assamblyName, nameOfB) as B;

            b.SetNumber(20);

            Console.WriteLine(“Number in class B is {0}”, B.Number);

        }

    }

}

答案输出两行,第一行是Number in class A is 10,而第二行是Number in class B is 20。上述C#代码先创建一个命名为NewDomain的应用程序域,并在该域中利用反射机制创建类型A的一个实例和类型B的一个实例。我们注意到类型A是继承自MarshalByRefObject,而B不是。虽然这两个类型的结构一样,但由于基类不同而导致在跨越应用程序域的边界时表现出的行为将大不相同。

      由于A继承MarshalByRefObject,那么a实际上只是在缺省的域中的一个代理,它指向位于NewDomain域中的A的一个实例。当a.SetNumber时,是在NewDomain域中调用该方法,它将修改NewDomain域中静态变量A.Number的值并设为20由于静态变量在每个应用程序域中都有一份独立的拷贝,修改NewDomain域中的静态变量A.Number对缺省域中的静态变量A.NewDomain没有任何影响。由于Console.WriteLine是在缺省的应用程序域中输出A.Number,因此输出仍然是10

    B只从Object继承而来的类型,它的实例穿越应用程序域的边界时,将会完整地拷贝实例。在上述代码中,我们尽管试图在NewDomani域中生成B的实例,但会把实例b拷贝到缺省的域。此时,调用b.SetNumber也是在缺省的域上进行,它将修改缺省的域上的A.Number并设为20。因此这一次输出的是20

问题(19运行下图中C代码,输出的结果是什么

int _tmain(int argc, _TCHAR* argv[])

{

    char str1[] = “hello world”;

    char str2[] = “hello world”;

 

    char* str3 = “hello world”;

    char* str4 = “hello world”;

 

    if(str1 == str2)

        printf(“str1 and str2 are same.\n”);

    else

        printf(“str1 and str2 are not same.\n”);

 

    if(str3 == str4)

        printf(“str3 and str4 are same.\n”);

    else

        printf(“str3 and str4 are not same.\n”);

 

    return 0;

}

答案输出两行。第一行是str1 and str2 are not same,第二行是str3 and str4 are same

str1str2是两个字符串数组。我们会为它们分配两个长度为12个字节的空间,并把“hello world”的内容分别拷贝到数组中去。这是两个初始地址不同的数组,因此比较str1str2的值,会不相同str3str4是两个指针,我们无需为它们分配内存以存储字符串的内容,而只需要把它们指向“hello world“在内存中的地址就可以了。由于“hello world”是常量字符串,它在内存中只有一个拷贝,因此str3str4指向的是同一个地址。因此比较str3str4的值,会是相同的

问题(20运行下图中C#代码,输出的结果是什么?并请比较这两个类型各有什么特点,有哪些区别

namespace Singleton

{

    public sealed class Singleton1

    {

        private Singleton1()

        {

            Console.WriteLine(“Singleton1 constructed”);

        }

        public static void Print()

        {

            Console.WriteLine(“Singleton1 Print”);

        }

        private static Singleton1 instance = new Singleton1();

        public static Singleton1 Instance

        {

            get

            {

                return instance;

            }

        }

    }

 

    public sealed class Singleton2

    {

        Singleton2()

        {

            Console.WriteLine(“Singleton2 constructed”);

        }

        public static void Print()

        {

            Console.WriteLine(“Singleton2 Print”);

        }

        public static Singleton2 Instance

        {

            get

            {

                return Nested.instance;

            }

        }

        class Nested

        {

            static Nested() { }

 

            internal static readonly Singleton2 instance = new Singleton2();

        }

    }

 

    class Program

    {

        static void Main(string[] args)

        {

            Singleton1.Print();

            Singleton2.Print();

        }

    }

}

答案: 输出三行:第一行“Singleton1 constructed”,第二行“Singleton1 Print”,第三行“Singleton2 Print”

当我们调用Singleton1.Print时,.NET运行时会自动调用Singleton1的静态构造函数,并初始化它的静态变量。此时会创建一个Singleton1的实例,因此会调用它的构造函数Singleton2的实例是在Nested的静态构造函数里初始化的。只有当类型Nested被使用时,才回触发.NET运行时调用它的静态构造函数。我们注意到我们只在Sington2.Instance里面用到了Nested。而在我们的代码中,只调用了Singleton2.Print。因此不会创建Singleton2的实例,也不会调用它的构造函数

这两个类型其实都是单例模式(Singleton)的实现。第二个实现Singleton2只在真的需要时,才会创建实例,而第一个实现Singleton1则不然。第二个实现在空间效率上更好。

 

博主何海涛对本博客文章享有著作权。网络转载请注明出处http://zhedahht.blog.163.com/

    原文作者:程序员面试题精选
    原文地址: http://zhedahht.blog.163.com/blog/static/254111742011299219769/
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞