C++常见面试题(三)

本博客内容:
一、C++初始化
二、什么是引用,使用引用要注意什么?
三、将引用作为函数返回值类型的格式、好处和规则
四、写出string类的类成员函数
五、main函数执行以前以及以后,还会执行什么代码?
六、分别写出bool、int、float,指针类型的变量“a”与零的比较语句?
七、this指针的用法以及意义
八、内联函数
九、友元
十、模板(template)

一、C++初始化

1.哪些东西必须放在初始化列表
常量成员
引用类型
没有默认构造函数的类类型。
2.没有默认构造函数的类有2种情况:
①一个类显式地声明了任何构造函数,编译器不会生成公有的默认构造函数
②一个类声明了private的默认构造函数,编译器不会生成公有的默认构造函数
比较安全高效的方法:尽量采用初始化列表
初始化顺序:成员是按照他们在类中出现的顺序进行初始化的。
为什么使用初始化列表:一是使用初始化列表, 二是在构造函数体内进行赋值操作。对于内置类型,使用2者区别不大。但是对于类类型来说,最好使用初始化列表(因为使用列表少了一次默认构造函数的过程)。

二、什么是引用,使用引用要注意什么?

1.引用就是某个变量的别名,对引用的操作与变量直接操作效果完全相同
2.必须初始化
3.声明之后不能该引用名作为其他变量名的别名
4.不时新定义一个变量,只是表示该引用名是目标变量名的一个别名,本身不是一种数据类型,本身不占存储单元,系统也不给引用分配存储单元
6.不能建立数组的引用

三、将引用作为函数返回值类型的格式、好处和规则

格式
类型标识符 & 函数名(参数){ }
好处:在内存中不产生被返回值的副本;正是该原因,所以不能返回一个局部变量的引用。
注意
1.不能返回局部变量的引用
2.不能返回函数内部new分配的内存的引用(因为会导致内存泄漏)
3.可以返回类的引用,但最好是const,否则将会导致类成员改变
4.流操作符重载返回值声明为“引用”的作用:
因为经常出现cout<<”hello”<

四、写出string类的类成员函数

class String
{
public:
    String(const char * str=NULL);  //通用构造函数
    String(const String & another); //拷贝构造函数
    ~String(); //析构函数
    String & operator=(const String &rhs); //赋值函数
private:
    char * m_data;//用于保存字符串
};
String::String(const char * str)
{
    if(str==NULL)
    {
        m_data=new char [1];
        m_data[0]='\0';
    }
    else
    {
        m_data=new char[strlen(str)+1];
        strcpy(m_data,str);
    }
}
String::String(const String & another)
{
    m_data=new char (strlen(another.m_data)+1);
    strcpy(m_data,other.m_data);
}
String & String ::operator=(const String & rhs)
{
    if(this==&this)
        return *this;
    delete [] m_data;//删除原来的数据
    m_data=new char [strlen(rhs.m_data)+1];
    strcpy(m_data,rhs.m_data);
    return * this;
}
String ::~String()
{
    delete [ ] m_data;
}

五、main函数执行以前以及以后,还会执行什么代码?

全局对象的构造函数在main函数之前执行,
全局对象的析构函数在main函数之后执行。而且main函数之后还会执行_onexit注册的函数。

int main()
{
    string str("fd");
    _onexit(fn1);
    _onexit(fn2);
    cout<<str<<endl;
    return 0;
    //先打印str,然后执行fn2函数,最后执行fn1函数
}

六、分别写出bool、int、float,指针类型的变量“a”与零的比较语句?

bool if(!a) 
int  if(a==0)
float     const expression exp=0.000001
        if(a<exp&& a>-exp)
pointer   if(a==NULL)

七、this指针的用法以及意义

1.this指针的用处
一个对象的this指针并不是对象的一部分,不会影响sizeof(对象)的结果。this作用域时在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。即使没写this指针,编译器在编译的时候也是加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均可通过this进行。
2.this指针的使用
1:类的非静态成员函数中返回类对象本身时,直接使用return *this;
2:当参数与成员变量名相同时,this->n=n;(不能写成n=n);

八、内联函数

1.必须与函数定义体放在一起才能使得函数称为内联,仅将inline放在函数声明前不起任何作用。
2.定义在类声明之中的成员函数自动称为内联
3.注意:
①内联函数必须要在调用该函数的每个文本文件中定义,建议将inline函数的定义放在头文件中。
②内联是以代码膨胀(复制)为代码的,仅仅省去了函数调用的开销,从而提高了
函数的执行效率。

九、友元

1.友元的三种形式
①友元函数
②成员函数可以是另一个类的友元

void test(Student &s)
{}
class Teacher
{
public:
void assignGrade();
}
//友元出现了
class Student
{
public:
friend void Teacher::assignGrade(Student & s);
friend void test(Student &s);
}

③友元类:
在teacher中用到了student类,使用student必须先声明,称为前向声明

//此处需要对Student的前向声明,否则Teacher不能认识Student
class Teacher{
private: Student * pList;
public:
    fun();
    }
    class Student{
    private:
        Teacher * pT;
        public:
friend class Teacher; //在学生类中把教师类声明为友元类

2.友元使用场合
1.运算符重载的某些场合
2.2个类要共享数据时
注意:
友元函数破坏了封装机制,尽量不使用成员函数,除非不得已时,才使用友元。
3.友元函数与类成员函数的区别?
1.成员函数有this指针,而友元函数无this指针
2.友元函数不能被继承。
友元类:可以访问原类的全部。放任何位置无所谓。

十、模板(template)

2种形式:类模板和函数模板

template<class T>
返回类型 函数名(形参列表){}
T min(T x,T y)
{return (x<y)?x:y;}

template<class T>
class 类名
{ 类定义;}

一般来说,非类型模板参数可以是常整数(包括枚举)或者指向外部连接对象的指针。就是说,浮点数是不行的,指向内部链接对象的指针是不行的
模板的特化与偏特化
:能够让你在模板的所有可能的实体中特化出一组子集。
a.模板的特化:

template<>
class Widgt<ModalDialog,MyControll>
{ 特化代码;}

b.模板偏特化:
模板特化是通过“给模板中的所有模板参数一个具体的类”的方式来实现的,而模板偏特化则是通过“给模板中的部分模板参数以具体的类”,而留下的模板参数仍然使用原来的泛化定义的方式来实现的。
偏特化能力强大:当你实例化一个模板时,编译器会把目前存在的偏特化与全特化比较,选出最合适的、最匹配的实现。
总结与注意
1.特化或偏特化时,template后面的尖括号不带任何内容;
2.特化/全特化是指给每一个模板参数一个具体的类型,以具体的实现这个模板,而且template后面的尖括号中不带任何内容;
偏特化是指只给部分模板参数一个具体的类型,来实现这个模板。
3.模板类型不是一种实类型,必须等到类绑定后才能确定最终类型,所以在实例化一个模板时,必须要能够让编译器看到在哪里使用了模板,而且必须看到模板的定义,而不仅仅是声明
建议:模板的声明和定义都放置在同一个.h文件中,这是最好
方法2:定义放源文件中(但不是所有的编译器都支持),或使用export,但也不是都支持。

//temp.h
template<class T>
class Temp
{
public:
void set_value(const T & rt);
private:
    T m_value;
};
template<class T>
void Temp<T>::set_value(const T & rt)
{
    m_value=rT;
}

参考:C++ Primer Plus
对于给定的函数名: 非模板函数>显式具体化>常规模板
见的最多是:隐式实例化
显式具体化:以template<>开头,并通过名称来指出类型。
举例:

//1普通版本
void Swap(int &a ,int &b);

//2常规模板版本(隐式实例化)
template<typename T>
    void Swap(T & a,T &b);

//显式实例化
template void Swap<int>(int ,int>; 
编译器将使用swap模板生成一个使用int类型的实例。
也就是使用swap模板生成int类型的函数定义。

//3显式具体化版本
template<>
    void swap<int>(int & ,int &);//此处的尖括号中int是可选项
意思:不要使用swap模板来生成函数定义,而应使用专门
为int类型显式地定义的函数定义,这些原型必须有自己的函数定义。
注意区别:需要在template后面加<>.

猜你喜欢

转载自blog.csdn.net/xiongluo0628/article/details/81877884