C++ 忘得差不多了

Published: 26 Sep 2015 Category: c_cpp

一、 C++中的类

  1. 默认的权限

    struct默认为public, class默认是private;

    继承权限也同上面的访问权限。

  2. 一个空类的默认函数

    a. 默认构造函数
    b. 析构函数 ~A() (默认的析构函数不能删除new运算符在自由存储器中分配的对象或对象成员。)
    c. 拷贝构造函数:

     CExample(const CExample&); //参数是const  对象的引用&
    

    d. 赋值函数
    每个类只有一个赋值函数
    由于并非所有的对象都会使用拷贝构造函数和赋值函数,程序员可能对这两个函数有些轻视。

     String a(“hello”);   
     String b(“world”);
     		String c = a; // 调用了拷贝构造函数,最好写成 c(a);
     c = b; // 调用了赋值函数
    
     // 赋值函数   
       	String & String::operator =(const String &other)
    

二、new、delete、malloc、free关系

delete会调用对象的析构函数,和会调用构造函数的new对应;free只会释放内存。malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。

delete与 delete []区别:delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。

三、子类析构时要调用父类的析构函数吗?

派生类的析构函数在执行完后,会自动执行基类的析构函数,这个是编译器强制规定的,没有为什么,甚至你在析构函数里调用return都不会立即返回到调用处,而是会先按顺序把析构函数全部调用完。

析构函数调用的次序是先派生类的析构后基类的析构,也就是说在基类的的析构调用的时候,派生类的信息已经全部销毁了。定义一个对象时先调用基类的构造函数、然后调用派生类的构造函数;析构的时候恰好相反:先调用派生类的析构函数、然后调用基类的析构函数。

四、常引用

string foo( );
void bar(string & s);

那么下面的表达式将是非法的:

bar(foo( ));
bar("hello world");

原因在于foo( )和”hello world”串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。引用型参数应该在能被定义为const的情况下,尽量定义为const。

五、结构与联合有什么区别?

(1). 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。 结构体的sizeof是所有成员的和,而联合体的sizeof等于其最长的成员的sizeof。 (2). 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。

#include <iostream>
using namespace std;

int main() 
{ 
    union test
    {
        int i;
        struct 
        {
            char first;
            char second;
        } half;
    } number;

    number.i = 0x4241;
    cout<<number.half.first<<" "<<number.half.second<<endl;
    number.half.first = 'a';
    number.half.second = 'b';
    printf("%x\n", number.i);

    return 0; 
}

输出结果:

A B
6261

六、函数返回引用

int  a=4;
int  &f(int  x){
    a=a+x;
    return a;
}

int main(void) {
    int t=5;
    cout<<f(t)<<endl;  // a = 9
    f(t)=20;           // a = 20
    cout<<f(t)<<endl;  // t = 5,a = 20  a = 25
    t=f(t);            // a = 30 t = 30
    cout<<f(t)<<endl;  // a = 60
}

将“引用”作为函数返回值类型的格式、好处和需要遵守的规则.

格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }
好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的. 注意事项:

(1)不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了”无所指”的引用,程序会进入未知状态。

(2)不能返回函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。

(3)可以返回类成员的引用,但最好是const。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

(4)流操作符重载返回值申明为“引用”,以及赋值操作符=。=这个操作符和流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。

七、C++是不是类型安全的?

答案:不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。

八、请说出const与#define 相比,有何优点?

const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。

2) 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。

九、简述数组与指针的区别?

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。

(1)修改内容上的差别

char a[] = “hello”;
a[0] = ‘X’;
char *p = “world”; // 注意p 指向常量字符串
p[0] = ‘X’; // 编译器不能发现该错误,运行时错误

(2) 用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

char a[] = "hello world";
char *p = a;
cout<< sizeof(a) << endl; // 12 字节
cout<< sizeof(p) << endl; // 4 字节

计算数组和指针的内存容量

void Func(char a[100])    {
    cout<< sizeof(a) << endl; // 4 字节而不是100 字节
}

栈内存与文字常量区

char str1[] = "abc";
char str2[] = "abc";

const char str3[] = "abc";
const char str4[] = "abc";

const char *str5 = "abc";
const char *str6 = "abc";

char *str7 = "abc";
char *str8 = "abc";

cout << ( str1 == str2 ) << endl;//0  分别指向各自的栈内存
cout << ( str3 == str4 ) << endl;//0  分别指向各自的栈内存
cout << ( str5 == str6 ) << endl;//1 指向文字常量区地址相同
cout << ( str7 == str8 ) << endl;//1 指向文字常量区地址相同

结果是:0 0 1 1

解答:str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域。

十、1int (*s[10])(int) 表示的是什么?

int (*s[10])(int) 函数指针数组,每个指针指向一个int func(int param)的函数。

十一、引用与指针有什么区别?

1) 引用必须被初始化,指针不必。

2) 引用初始化以后不能被改变,指针可以改变所指的对象。

3) 不存在指向空值的引用,但是存在指向空值的指针。

十二、同一进程下的线程不可以共享的是

A. stack
B. data section
C. code section
D. file fd A.  同一进程下的进程由自己独立的栈空间,因此不可以共享栈段;

线程可以共享的资源:

  • 地址空间
  • 全局变量
  • 打开的文件
  • 子进程
  • 信号及信号服务程序

线程不可以共享的资源:

  • PC的值
  • 寄存器
  • 栈段
  • PSW的值