c++ 面试 get

1.构造函数、析构函数和虚函数的调用顺序

class Base
{
    virtual void method()
    {
        cout<<"from Base"<<endl;
    }
public:
    Base()
    {
        cout<<"Base struct"<<endl;
    }
    virtual ~Base()
    {
        method();
    }
    void baseMethod()
    {
        method();
    }
};
class B:public Base
{
    void method()
    {
        cout<<"from B"<<endl;
    }
public:
    B()
    {
        cout<<"B struct"<<endl;
    }
    ~B()
    {
        method();
    }
};
int main()
{
    Base *base=new B;
    base->baseMethod();
    delete base;
    return 0;
}

运行结果:

2.函数返回指针

int* fun(int a[])
{
    static int c[10];//C++ 允许您从函数返回指针,但是,C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量;你可以试试去掉static
    for(int i=0;i<5;i++)
        c[i]=a[i];
    return c;
}

int main()
{
    int d[6]={1,3,2,5,4};
    int* c=fun(d);
    for(int i=0;i<5;i++)
        cout<<*(c+i)<<" ";
    cout<<endl;
}

 

运行结果:

3.指针,指针数组的使用

 

int main()
{
    char *p[1]={"hello"};
    printf("%s\n",(p)[0]);
    int as[5]={1,2,3,4,5};
    int *ptr=(int*)(&as+1);
    printf("%d,%d\n",*as+1,*(ptr-1));
}

运行结果:

4.char类型赋值int型,输出%d格式(小于128的数值,直接隐式转换为相应数字,不小于128的数字输出其对应的值对128取余再加-128)

int main()
{
    char a,b,c,d;
    a=127,b=128,c=197,d=500; //b=128%128+(-128),c=197%128+(-128)......
    printf("a=%d,b=%d,c=%d,d=%d\n",a,b,c,d);
    return 0;
}

运行结果:

5.无符号整形(负数时转换:2^32-(负数))

int main()
{
    cout<<(25u-50)<<endl;
}

运行结果:

 

6.\r \n 的使用:

'\n'是新行键,光标换到下一行;'\r'是回车键,光标移动到本行最左端位置,后边如果还有字符就会从首位开始覆盖

int main()
{
    printf("crazefor\rcode\n");
    printf("abcd\r123\n");
    printf("123\r456\r789\n");
    printf("cat\rdog\n");
    printf("123\n456\n789\r");
    return 0;
}

运行结果:

7.字符数组和字符指针的比较

int main()
{
    char cc[20]="hello,world";
    char dd[20]="hello,world";
    cout<<(cc==dd)<<endl;//地址不同,况且不能这么比吧,不然要strcmp和string干嘛-_-
    if(!strcmp(cc,dd))
        cout<<"yes1"<<endl;
    char* aa="hello,world";
    char* bb="hello,world";
    cout<<(aa==bb)<<endl;//地址相同,但是不建议这么比较,老实用strcmp()吧
    if(!strcmp(aa,bb))
        cout<<"yes2"<<endl;
    string gg="hello,world";
    string hh="hello,world";
    cout<<(gg==hh)<<endl;//可以这么比
    return 0;
}

运行结果:

8.throw 异常

class A{
public:
    A(){}
    ~A(){
        cout<<"destory A"<<endl;//如果析构函数里没有catch异常(即只throw,没有try,catch),代码运行时错误
        try{
            throw 42;
        }catch(int a){
            cout<<a<<endl;
        }
//        throw 42;
    }
};
int main()
{
    try
    {
        A aaa;
        throw 32;
    }
    catch(int aa)
    {
        cout<<aa<<endl;
    }
    return 0;
}

运行结果:

 

(i)、程序接受到throw语句后就会自动调用析构器,把该域(try后的括号内)对象clean up,然后再进
入catch语句(如果在循环体中就退出循环)。

这种机制会引起一些致命的错误,比如,当“类”有指针成员变量时(又是指针!),在 “类的构建器
”中的throw语句引起的退出,会导致这个指针所指向的对象没有被析构。这里很基础,就不深入了,提
示一下,把指针改为类就行了,比如模板类来代替指针,在模板类的内部设置一个析构函数。

 

9.==和前置++、后置++的优先级(先单目后双目运算符)

 

int main()
{
    int a=10;
    if(a==a--)
        printf("True1\n");
    a=10;
    if(a==--a)
        printf("True2\n");
    a=10;
    if(a>--a)
        printf("True3\n");
    else
        printf("False3\n");
    return 0;
}

运行结果:

 

扩展:

 

C语言中运算符种类比较繁多,优先级有15种,结合性有两种。    
    如何记忆两种结合性和15种优先级?下面讲述一种记忆方法。    
    结合性有两种,一种是自左至右,另一种是自右至左,大部分运算符的结合性是自左至右,只有单目运算符、三目运算符的赋值运算符的结合性自右至左。    
    优先级有15种。记忆方法如下:    
    记住一个最高的:构造类型的元素或成员以及小括号。    
    记住一个最低的:逗号运算符。    
    剩余的是一、二、三、赋值。    
    意思是单目、双目、三目和赋值运算符。    
    在诸多运算符中,又分为:    
    算术、关系、逻辑。    
    两种位操作运算符中,移位运算符在算术运算符后边,逻辑位运算符在逻辑运算符的前面。再细分如下:    
    算术运算符分     *,/,%高于+,-。    
    关系运算符中,〉,〉=,<,<=高于==,!=。    
    逻辑运算符中,除了逻辑求反(!)是单目外,逻辑与(&&)高于逻辑或(||)。    
    逻辑位运算符中,除了逻辑按位求反(~)外,按位与(&)高于按位半加(^),高于按位或(|)。    
    这样就将15种优先级都记住了,再将记忆方法总结如下:    

 

    去掉一个最高的,去掉一个最低的,剩下的是一、二、三、赋值。双目运算符中,顺序为算术、关系和逻辑,移位和逻辑位插入其中。

 

10.成员变量初始化的两种方法比较

a.通过在构造函数内赋值

class Point
{
public:
 Point(){ _x = 0; _y = 0;};
 Point( int x, int y ){ _x = 0; _y = 0; }
private:
 int _x, _y;
};

 

b.使用初始化列表

 

 class Point
{
public:
 Point():_x(0),_y(0){};
 Point( int x, int y ):_x(x),_y(y){}
private:
 int _x, _y;
};

 

 

必须使用初始化列表的情况:1.const和引用数据成员被初始化时;2.需要初始化的数据成员是对象的情况(这里包含了继承情况下,通过显示调用父类的构造函数对父类数据成员进行初始化);

 

 

另外:随着类越来越大,越来越复杂,它们的构造函数也越来越大而复杂,那么对象创建的代价也越来越高,所以一般情况下建议使用初始化列表进行初始化,不但可以满足const和引用成员的初始化要求,还可以避免低效的初始化数据成员。

注:构造函数列表的初始化方式不是按照列表的的顺序,而是按照变量声明的顺序。

11.static 类型(分配在全局区,只分配一次,int类型初始值为0)

int caseNum=1;
int main()
{

    cout<<"in main"<<endl;
    static int x; //如果去掉static,分配一个随机数
    if(x++<2)
    {
        cout<<"x: "<<x<<endl;
        cout<<"case: "<<caseNum++<<endl;
        main();
    }

    return 0;
}

运行结果:

12.动态分配一维、二维数组

//分配一维:

//malloc-free:

int len;

int *p;

cin>>len;

p=(int *)malloc(len*sizeof(int);

free p;

//new-delete:

p=new int[len];

delete []p;

//分配二维:

//malloc-free:

int row,col;

cin>>row>>col;

int **p=(int **)malloc(row*sizeof(int*));

for(int i=0;i<row;i++)

*(p+i)=(int *)malloc(col*sizeof(int));

for(int i=0;i<row;i++)

free(*(p+i));

//new-delete:

int **p=new int*[row];

for(int i=0;i<row;i++)

p[i]=new int[col];

delete []p;

//使用vector:

vector<vector<int> >p(row,vector<int>(clo));

for(int i=0;i<row;i++)

for(int j=0;j<col;j++)

cin>>p[i][j];

13.链表逆序

核心操作图示:

    public static Node reverse(Node head) {  
        if(head==null || head.next==null) return head;  
        Node p1 = head;  
        Node p2 = head.next;  
        p1.next = null;  
        while(p2.next != null) { 
            Node p3 = p2.next;
            p2.next = p1;  
            p1 = p2;  
            p2 = p3;
        }  
        p2.next = p1;  
        return p2;  
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值