编程语言相关知识点梳理

c++语言常考点

1、 c++中的空类, 默认产生哪些类成员函数?

默认构造函数,拷贝构造函数,析构函数,赋值运算符(operator =),取址运算符(operator &)(一对,一个非const,一个const)。

class Empty{
   
public: 
Empty();  //缺省构造函数
    Empty(const Empty&);  //拷贝构造函数
    ~Empty();  //析构函数
    Empty &operator = (const Empty &);  //赋值运算符
    Empty *operator &();  //取址运算符
    const Empty* operator & () const;  //取址运算符const
}

问题:哪些情况下会调用拷贝构造函数?
(1)一个对象以值传递的方式传入函数体
(2)一个对象以值传递的方式从函数返回
(3)一个对象需要通过另外一个对象进行初始化
其实拷贝构造函数是由普通构造函数和赋值操作符共同实现的。

2、 单例模式的特点是什么?用c++实现单例模式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问。
注意
(1)单例类只能有一个实例
(2)单例类必须自己创建自己的唯一实例
(3)单例类必须给所有其他对象提供这一实例

单例模式需满足两个条件:
(1)保证一个类只创建一个实例
(2)提供一个对该实例的全局访问点

如果系统有类似的实体(有且只有一个,且需要全局访问),那么就可以将其实现为一个单例。实际工作中常见的应用举例:
(1)日志类:一个应用往往只对应一个日志实例
(2)配置类:应用的配置集中管理,比提供全局访问
(3)管理器:比如windows系统的任务管理器就是一个例子,总是只有一个管理器的实例
(4)共享资源类:加载资源需要较长时间,使用单例可以避免重复加载资源,并被多个地方共享访问。

c++实现单例模式
(1)懒汉模式
singleton在程序第一次调用的时候才会初始化

class Singleton{
   
private:
    static Singleton* instance;
    Singleton(){};
public:
    static Singleton* GetInstance(){
        if(NULL == instance){
            instance = new Singleton();
        }
        return instance;
    }
};
Singleton* Singleton::instance = NULL;
//静态数据成员是静态存储的,必须对它进行初始化

使用该模式时,由于if语句的存在,会影响调用的效率。而且,在多线程环境下使用时,为了保证只能初始化一个实例,需要用锁来保证线程安全性,防止同时多个线程进入if语句中。

加入double-check,代码变为:

class Singleton{
   
private:
    static Singleton* instance;
    Singleton(){};
public:
    static Singleton* GetInstance(){
        if(NULL == instance){
            Lock(); //借用其他类来实现,如boost
            if(NULL == instance){
                instance = new Singleton();
            }
            Unlock();
        }
        return instance;
    }
};
Singleton* Singleton::instance = NULL;

Lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待。(即被阻止),直到该对象被释放。

如果处理大量数据时,锁会成为整个性能的瓶颈。
一般懒汉模式适用于程序一部分中需要使用Singleton,且在实例化后没有大量频繁访问或线程访问的情况。

(2)饿汉模式
无论是否调用该类的实例,在程序开始时就会产生一个该类的实例,并在以后仅返回此实例。
由静态初始化实例保证其线程安全性。因为静态实例初始化在程序开始时进入主函数之前就由主线程以单线程方式完成了初始化。不必担心多线程问题。
故在性能需求较高时,应使用这种模式,避免频繁的锁争夺。

class Singleton{
private:
    static const Singleton* instance;
    Singleton(){};
public:
    static const Singleton* GetInstance(){
        return instance;
    }
};
const Singleton* Singleton::instance=new Singleton();

静态初始化的方式是在自己被加载时就将自己实例化,所以被形象地称之为饿汉式单例类。
之前的处理方式是要在第一次被引用时,才会将自己实例化,所以就被称为懒汉式单例类。

3、 c++中不同数据类型所占用的内存大小?

单位都为字节 32位机器 64位机器
char 1 1
int 4 大多数是4,少数是8
short 2 2
long 4 8
float 4 4
double 8 8
指针 4 8

4、 编写类String的构造函数、 析构函数和赋值函数。

已知类String的原型为:

class String{
public:
    String(const char *str=NULL);  //普通构造函数
    String(const String &other);   //拷贝构造函数
    ~String(void);   //析构函数
    String &operator = (const String &other);  //赋值函数
private:
    char *m_data;  //用于保存字符串
}

解答:
//普通构造函数

String::String(const char *str=NULL){
    if(str == NULL){
        m_data = new char[1];
//得分点,对空字符串自动申请存放结束标志'\0'
        *m_data='\0';
    }else{
        int length = strlen(str);
        m_data = new char[length+1];
        strcpy(m_data, str);
    }
}

//String的析构函数

String::~String(void) {
    delete[] m_data;
}

//拷贝构造函数

String::String(const String &other) {
    //得分点,输入参数为const型
    int length = strlen(other.m_data); 
    m_data = new char[length + 1];  //加分点,对other.m_data做NULL判断
    strcpy(m_data, other.m_data);
}

//赋值函数

String & String::operator=(const String &other) {
    if(this == &other){
        return *this;
    }
    delete[] m_data;  //得分点,释放原有的内存资源
    int length = strlen(other.m_data);  
    m_data = new char[length+1];  //加分点,对m_data加NULL判断
    strcpy(m_data, other.m_data); 
    return *this;  //得分点,返回本对象的引用
}

5、 引用和指针有什么区别?

本质上:引用是别名,指针是地址
具体的:
(1)引用在创建的同时必须被初始化,不能有NULL引用,引用只能在定义时被初始化一次,之后不可变。指针可以在运行时改变其指向的值。
(2)从内存上看,指针是一个实体,指针会分配内存区域。而引用不会,它仅仅是一个别名。
(3)引用不能为空,指针可以为空。
(4)引用没有const,指针有const,const的指针不可变。
(5)“sizeof引用”得到的是所指向的变量(对象)的大小,而“sizeof指针”得到的是指针本身的大小。
(6)指针和引用的自增(++)运算意义不一样。

例子:

int a = 0;
int &b = a;  //b是a的一个引用,b只是a的一个别名,和a一样使用。
int *p = &a;  //p是a的一个指针
cout<<b<<endl;
b++;
cout<<b<<endl;
cout<<p<<endl;
p++;
cout<<p<<endl;

输出:
0
1
0x7fff59fdeb98
0x7fff59fdeb9c

关于引用:
引用:就是某一个变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。
引用的声明方法:类型标识符 &引用名 = 目标变量名
例如引用a:
int a;
int &ra = a;
注意:(1)&在此不是求地址运算符,而是起标识作用
(2)类型标识符是指目标变量的类型
(3)声明引用时,必须同时对其进行初始化
(4)引用声明完毕后,相当于目标变量有两个名称即该目标原名称和引用名,且不能再把该引用作为其他变量的别名。
(5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址,&ra与&a相等。
(6)不能建立数组的引用,因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。

6、 c++如何连接数据库?

连接数据库大致分如下四个步骤:
(1)初始化
(2)用Connection对象连接数据库
(3)利用建立好的连接,通过Connection、Command对象执行SQL命令,或利用Recordset对象取得结果记录集进行查询、处理
(4)使用完毕后关闭连接释放对象

7、 c++模板

模板是c++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。
模板是一种对类型进行参数化的工具
通常有两种形式:函数模板和类模板

函数模板针对仅参数类型不同的函数
类模板针对仅数据成员和成员函数类型不同的类

使用模板的目的就是能够让程序员编写与类型无关的代码。

注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。

(1)函数模板
template < class 形参名, class 形参名,….. > 返回类型 函数名(参数列表)
{
函数体
}

例子:
比如swap的模板函数形式为:
template < class T > void swap(T&a, T&b){};
当调用这样的模板函数时类型T就会被调用时的类型所代替,比如swap(a, b)其中a和b是int 型,这时模板函数swap中的形参T就会被int 所代替,模板函数就变为swap(int &a, int &b)。而当swap(c, d)其中c和d是double类型时,模板函数就会被替换为swap(double &a, double &b),这样就实现了函数的实现与类型无关的代码。

(2)类模板
template < class 形参名, class 形参名, … > class 类名
{ … };

一旦声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,即可以在类中使用内置类型的地方都可以使用模板形参名来声明。

例子:
template < class T > class A {
public :
T a;
T b;
T hy(T c, T &d);
};
在类A中声明了两个类型为T的成员变量a和b,还声明了一个返回类型为T带两个参数类型为T的函数hy。

8、 c++中new、 malloc的区别?

(1)申请的内存所在位置
new操作符从自由存储区上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。
自由存储区是c++基于new操作符的一个抽象概念。自由存储区可以是堆,也可以是静态存储区,这都看operator new在哪里为对象分配内存。

(2)返回类型安全性
new操作符内存分配成功时,返回的是对象类型的指针。类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。malloc内存分配成功则是返回void ,需要通过强制类型转换将void 指针转换成我们需要的类型。

(3)内存分配失败时的返回值
new内存分配失败时,会抛出bad_alloc异常,它不会返回NULL,malloc分配内存失败时返回NULL。

(4)是否需要指定内存大小
使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。
malloc则需要显式地指出所需的尺寸。
例子:
A *ptr = new A;
A ptr = (A*)malloc(sizeof(A)); //需要显式指定所需内存大小sizeof(A)

(5)是否调用构造函数/析构函数
new/delete会调用对象的构造函数/析构函数从完成对象的构造/析构。malloc则不会调用。

(6)对数组的处理
c++提供了new[]与delete[]来专门处理数组类型。使用new[]分配的内存必须使用delete[]进行释放,两个要配套使用,不然会出现数组对象部分释放的现象,造成内存泄漏。
malloc动态分配一个数组的内存,需要手动自定数组的大小。
int ptr = (int )malloc(sizeof(int)*10);
//分配一个10个int元素的数组

(7)new与malloc是否可以相互调用
operator new/operator delete的实现可以基于malloc。而malloc的实现不可以去调用new。

(8)是否可以被重载
operator new/operator delete可以被重载。malloc/free并不允许重载

(9)能够直观地重新分配内存
使用malloc分配内存后,如果在使用过程中发现内存不足,可以使用realloc函数进行内存重新分配实现内存的扩充。new没有这样直观的配套设施来扩充内存。

(10)客户处理内存分配不足
在operator new抛出异常以反映一个未获得满足的需求之前,它会先调用一个用户指定的错误处理函数,这就是new_handler。对于malloc,客户并不能够去编程决定内存不足以分配时要干什么,只能看着malloc返回NULL。

9、 c++中头文件(.h)和源文件(.cpp)的区别?

头文件(.h)
写类的声明(包括类里面的成员和方法的声明)、函数原型、#define常数等,但一般来说不写出具体的实现。
源文件(.cpp)
主要写实现头文件中已经声明的那些函数的具体代码。需要注意的是,开头必须#include一下实现的头文件,以及要用到的头文件。那么当你需要用到自己写的头文件中的类时,只需要#include进来就行了。

10、 头文件中的#ifndef、 #define、 #endif的作用?

(1)作用:防止该头文件被重复引用。
“被重复引用”是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的。比如:a.h文件#include“c.h”,而此时b.cpp文件导入了#include “a.h”和#include“c.h”,此时就会造成c.h重复使用。

(2)头文件被重复引用引起的后果。
有些头文件重复引用只是增加了编译工作的工作量,不会引起太大的问题,仅仅是编译效率低一些,但是对于大工程而言编译效率低下那将是一件多么痛苦的事情。
有些头文件重复包含,会引起错误。比如在头文件中定义全局变量(虽然这种方式不被推荐,但确实是c规范允许的)这种会引起重复定义。

(3)是不是所有的头文件中都要加入#ifndef、#define、#endif这些代码?
不一定要加,但是不管怎样,用ifndef…或者其他方式避免头文件重复包含,只有好处没有坏处。
例子:

#ifndef GRAPHICS_H   //防止 graphics.h被重复引用
#define GRAPHICS_H
#include<math.h>  //引用标准库的头文件
#include“header.h”   //引用非标准库的头文件
...
void Function(...);   //全局函数声明
...
class Box{   //类结构声明
...
};
#endif

11、 c++编译过程中的各个阶段都干了啥?

编译一般分为四个步骤:
预处理->编译->汇编->链接
gcc认为预处理的文件是c文件,并且设定c形式的连接
g++认为预处理的文件是c++文件,并且设定c++形式的连接

(1)编译预处理
预处理过程主要处理那些源文件中的以”#”开始的预编译指令,主要处理规则有:
1. 将所有的”#define”删除,并展开所用的宏定义
2. 处理所有条件预编译指令,比如“#if”、“#ifndef”、“#elif”、“#endif”。
3. 处理”#include”预编译指令,将所包含的文件插入到该编译指令的位置,注:此过程是递归进行的。
4. 删除所有注释
5. 添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息以及用于编译时产生编译错误或警告时可显示行号。
6. 保留所有的#pragma编译器指令。(pragma的作用是设定编译器的状态或者是指示编译器完成一些特定的动作)

(2)编译
编译过程就是把预处理完的文件进行一

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值