经典问题_狄泰总结


1)int f() 与int f(void) 有区别吗? 如果有区别是什么? //面试小问题 要看用什么编译器:如果是用c编译器类型.如果是c++编译器
c 语言:
int f():返回值为int,参数为任意的函数,二义性
int f(void):返回值为int的无参(不接受任何参数)函数,
c++:
返回值是int的无参(不接受任何参数)函数
//c语言的默认类型在c++是不合法的,c++不允许不写返回值类型
c 语言缺省为int类型

2)面试题:引用有自己的存储空间吗?
会的?=>功能像指针?==>本质是指针 ==>占用的空间大小与指针一样

经典问题二:

a)当程序存在多个对象的时候,如何确定这些对象的析构顺序?

1)单个对象创建时的构造函数:(先父母,后客人,再自己),
析构函数与对应的构造函数的调用顺序相反

调用父类构造函数
调用成员变量的构造函数
调用类自身的构造函数

2)多个不同的析构顺序:析构顺序与构造顺序 相反
eg:构造顺序 ABC => 析构顺序 CBA

3)栈对象和全局对象,类似于入栈和出栈,最后构造的顺序,最新被析构
4)堆对象的析构 发生在delete的时候,与delete的使用有关

b)const 关键字能否修饰类的对象,如果可以,有什么特性

b.1)const 对象

1)可以,对象从某种程度来说也是变量,const关键字能修饰对象
2)const 修饰的对象 为只读对象 ,const 对象 所对应成员变量的值是不能被改变的
3) const 对象 只能调用const 成员函数
4) 只读的意思(在编译时不能被改变,在运行时可以被改变,用指针来改变)
5)在实际工程开发中,只有很少的对象使用const 对象

const Test t(1);	//const 对象

b.2)const 成员函数

1)const 对象只能调用const成员函数
2)const 成员函数 =》 被const对象调用
3)const 成员函数里面不能直接改写成员变量的值,m_value ++ 都不可以
4)在拷贝构造函数里面 Test::Test(const Test& t)

	Test::Test(const Test& t)		// t =>这个引用是const 引用 
{
	mi = t.get Mi()//所以这个getMi 也是需要const定义
	=>mi = t.mi
}

5)const 成员函数的定义 :在函数声明/定义的后面加上const

class Test
{
int getMi()const;
}
Test::getMi()const
{
	//mi =2  失败,在const成员函数里面不能修改成员函数的值
	return Mi
}

c)成员函数和成员变量都隶属于具体对象的吗?

答1: 每一个对象拥有自己独立的属性(成员变量)
答2: 所有的对象共享一套方法(成员函数)(原因:成员函数存放在代码段,代码段不能动态删除的)
答3: 成员函数能够直接访问对象的属性?
问3: 成员函数是怎么分辨是来自哪个具体对象调用的
答4:通过隐藏的this 指针,编译器会向成员函数隐式传递this指针,this指针指向了该具体对象的地址,隐藏的this指针用于表示当前对象

=》static 静态成员函数没有this 指针

经典问题三

a)什么时候需要重载赋值操作符?编译器是否提供默认的赋值操作符?

答1:当用到系统资源的时候(打开问题,在堆内存创建对象,等)时,需要深拷贝,需要重载拷贝构造函数和赋值操作符重载函数,保持逻辑状态一致,指向两个不同的内存地址

答2:编译器提供了默认的赋值操作符,但只完成浅拷贝的动作,浅拷贝后,物理逻辑相同,两个指针均指向同一个地址

	Test& opeateor = { const Test& obj}		//重载赋值操作符必须有以下条件
	{
			 if (this != &obj 
			 {
			 	//深拷贝
			 }
			 return *this
			/* 1)返回值为引用 Test&  ,便于连续赋值
				2)参数为 const Test&   参数为const 引用
				3)	判断是否为自复值?    if (this != &obj 
				4)返回 值
			*/
	}	

b)这个类里面没东西,是否为空?

不是,编译器会自动为他提供构造函数 、拷贝构造函数 赋值操作函数 析构函数
class Test
{

}

等价于
clase Test
{
Test(); //构造函数
~ Test(); //析构函数
Test(const Test& t); //拷贝构造函数
Test& opeateor (const Test& ); //赋值操作符重载函数
}

**

c)下面的字符串输出什么?为什么不对?标准库有bug?

答:明明用了c++ string类,却又采用了c语言的用法,混合使用了c和c++,这样最容易出现不可预料的问题 ==>c++开发尽量避开c语言中惯用的编程思想
string对象内部维护了一个指向数据的char*指针,这个指针可能在程序运行中发生改变

案例1:
string s= "12345";
const char *p =s.c_str();	//用了 C语言 ,这里返回c语言方式的字符串
cout  << p <<endl;
s.append("abced");			//p成为了字符串
cout << p << endl


应该直接这样用:
string s= "12345";
string p=s;
s. append("abced");

案例2const char* p = "12345";
string s= ""
s.reserve(10);
for(int i=0; i<5; i++)
{
		s[i] = p[i];				//证明这里成功赋值了
}
if(s.empty() )			//但这里却为空 ,问题还是由于用了混合编程
{
	cout << s<<endl;	
}

应该直接改成这样:

const string p = "12345";
string s= ""
s.reserve(10);
s =p;
cout << s<<endl;

经典问题五

小结
C++支持变参函数
变参函数无法很好处理对象参数
利用函数模板和变参函数能够判断指针变量
构造函数和析构函数不要抛出异常

a)企业面试题1,编写一个程序判断一个变量是不是指针

指针是变量
指针保存的值:是某个地址

知识点:
1)c++ 支持c语言的变参函数
2)c++编译器匹配的调用优先级:
普通成员函数 > 函数重载 > 变参函数

思路:
1)将一个变量分为 指针和非指针
2) 编写函数:
指针变量调用时 返回true
非指针变量调用时 返回false

代码实现:利用模板的部分特化(指针特化),编译器优先匹配函数模板利用模板的特化

问题2)基本类型没问题,你可以自定义类类型吗?
问题2=》变参函数(C语言的东西)无法解析对象参数,可能造成程序崩溃
进一步挑战: 如何让编译器精确匹配函数,但不进行实际的调用
用到的知识点 c语言 sizeof 宏定义
class Test
{
public:
    Test()
    {
    }
    virtual ~Test()
    {
    }
};

问题1)编写一个程序判断一个变量是不是指针
template 
< typename T>
bool IsPtr( T* v)			//指针部分特化
{
	return true
}

//bool IsPtr(...)			//  变参函数 ...代表任意参数	变参函数->c语言的东西
int IsRtr			//针对问题2改变类型
{
		return false
}

//#define ISPTR(p) (sizeof(IsPtr(p)) == sizeof(char))	//定义的这个宏,判断调用Isptr的类型是不是bool 一个字节   ,在编译阶段

int main
{
	//针对变量1编写一个程序判断一个变量是不是指针
	int i=0;
	int *p =&i;			//这里满足了问题1基本类型需求
	cout << "p is a pointer: " << IsPtr(p) <<endl;
	cout << "i is a pointer: " << IsPtr(i) <<endl;
	
	//针对问题2,基本类型没问题,你可以自定义类类型吗?
	   Test t;
    Test* pt = &t;
    //直接这样使用,会系统出错,变参函数(C语言的东西)无法解析对象参数,可能造成程序崩溃
    // cout << "pt is a pointer: " << IsPtr(pt) << endl;    // true	
    //cout << "t is a pointer: " << IsPtr(t) << endl;    // false
    //这里实现只编译不调用,利用了sizeof 判断isptr类型大小,而不使用,而且利用了宏定义,在预处理阶段就替换了
    cout << "pt is a pointer: " << ISPTR(pt) << endl;    // true
    cout << "t is a pointer: " << ISPTR(t) << endl;    // false
}

b)企业面试题2:问1)构造函数中抛出异常会怎样?

构造函数抛出异常会:
1)构造函数立即停止
2)无法创建对象
3)不会调用析构函数
4)对象所占用的空间立即收回

=》建议:在构造函数中不要抛出异常?
接着问2):那这些可能出现异常的代码怎么处理
答:利用二阶构造模式,将可能产生异常的代码放在二阶构造函数里面

问3)那析构函数中抛出异常会怎样?
答:析构函数中抛出异常会导致无法晚上释放内存空中,产生内存泄漏的风险

案例:构造函数抛出异常
#include <iostream>
#include <string>

using namespace std;

class Test
{
public:
    Test()
    {
        cout << "Test()" << endl;
        
        throw 0;	//这里会立即返回
    }
    virtual ~Test()
    {
        cout << "~Test()" << endl;
    }
};


int main(int argc, char *argv[])
{
	/*//这里先将p指针初始化为1,证明构造函数抛出异常后,对象创建不成功,new不会返回任何值,包括null
	*/
    Test* p = reinterpret_cast<Test*>(1);	
    
    try
    {
        p = new Test();	
    }
    catch(...)	//	...表示捕获任何异常
    {
        cout << "Exception..." << endl;	
    }
    
    cout << "p = " << p << endl;	//输出p=1,且不会输出析构函数
    
    return 0;

linux 内存检测工具 valgrind

valgrind --tool=memcheck --leak-check=full ./a.out => 做内存方面的检查 ,检测对象: a.out

拾遗:令人迷惑的写法(不常见的写法)

写法1:class用来在模板中定义泛指类型
来源:历史遗留,当时c++ 用class来进行代码复用
缺点:带来了二义性 => 后面更新用 typename(同时typename 可以消除class带来的二义性)
typename 的作用:
a) 在模板定义时表面泛指类型
b)明确告诉编译器其后的标识符为类型

#include <iostream>
#include <string>

using namespace std;

//在古老的代码里可能会出现,历史遗留问题 ,这种容易让人以为这个是 特制类类型
template < class T >	//令人迷惑的写法,class用来在模板中定义泛指类型,跟typename一样的效果
class Test
{
public:
    Test(T t) 
    { 
        cout << "t = " << t << endl;
    }
};

template < class T >
void func(T a[], int len)
{
    for(int i=0; i<len; i++)
    {
        cout << a[i] << endl;
    }
}


//
//
二义性案例:
int a = 0;

class Test_1
{
public:
    static const int TS = 1;
};

class Test_2
{
public:
    struct TS
    {
        int value;
    };
};

template
< class T >
void test_class()
{
 单纯这样写会产生二义性: TS 会让人觉得是数据类型 或 静态成员变量
    错误写法: T::TS * a;        // 1. 通过泛指类型 T 内部的数据类型 TS 定义指针变量 a (推荐的解读方式)
                      // 2. 使用泛指类型 T 内部的静态成员变量 TS 与全局变量 a 进行乘法操作
      typename T::TS * a	//正确写法用typename 声明 T:TS 是一个类型(类似Test2里的struct TX)而不是静态成员变量
}


int main(int argc, char *argv[])
{
    // test_class<Test_1>();
    test_class<Test_2>();
    
    return 0;
}

写法2:try…catch 将函体分成2部分 + 异常声明

a)try…catch 将直接将函体分成2部分,分隔正常功能和异常功能代码
b)函数声明和定义时可以直接指出可能抛出的异常声明
=》异常声明成为函数的一部分可以提供代码的可读性
=》函数异常声明是与编译器的一种约定,违反约定将直接导致运行终止
=》可以直接通过异常声明定义无异常函数

#include <iostream>
#include <string>

using namespace std;

/*int func(int i, int j) throw(int)		//如果这里声明抛出的是int类型,确抛出char型,相当于违反了
与编译器的约定,编译ok,执行直接异常终止,哪怕后面 catch(...任意类型)
*/
//函数定义时抛出异常声明,抛出的异常类型只能为 int类型和char类型
int func(int i, int j) throw(int, char)		
{
    if( (0 < j) && (j < 10) )
    {
        return (i + j);
    }
    else
    {
        throw '0';
    }
}

void test(int i) try	//try..catch 将直接将函体分成2部分,分隔正常功能和异常功能代码
{
    cout << "func(i, i) = " << func(i, i) << endl;
}
catch(int i)
{
    cout << "Exception: " << i << endl;
}
catch(...)
{
    cout << "Exception..." << endl;
}


int main(int argc, char *argv[])
{
    test(5);
    
    test(10);
    
    return 0;
}

案例:用最高效的方式来求 1+2+3+…+N(模板技术)

  1. for循环

2)等差数列

3)模板技术

依赖技术: 类模板技术 ,模板完全特化技术 数值型参数模板

#include <iostream>
#include <string>

using namespace std;

template
< typename T, int N >	//数值型模板参数
void func()
{
    T a[N] = {0};
    
    for(int i=0; i<N; i++)
    {
        a[i] = i;
    }
    
    for(int i=0; i<N; i++)
    {
        cout << a[i] << endl;
    }
}

template
< int N >
class Sum	//递归定义
{
public:
	//static const int VALUE =0 (const int VALUE =0)定义一个常量,用static来限定
	/*因此要么存储在全局数据区,要么放在符合表 => value值会直接进入符合表,
	由于static 修饰,value会存放在全局数据区  => 证明解决
    static const int VALUE = Sum<N-1>::VALUE + N;  //递归定义 Sum<N-1>::VALUE + N;编译器会先去求Sum<N-1>::VALUE 的值*/
};

template
< >
class Sum < 1 >		//sum 模板类的特化实现(完全特化)
{
public:
    static const int VALUE = 1;
};


int main()
{
/*这样最高效,只用了一条语句,计算过程在编译阶段,编译完成后,结果就确定了, Sum<10>::VALUE既没有加减乘除操作,也没有函数调用过程
VALUE是一个常量,在编译阶段已经确认了
	当遇到Sum<10>::VALUE时,编译器会去template < int N >class Sum 里去求值,后面发现是递归求值,不断往下走,直到遇到完全特化 template< > class Sum < 1 > ,最终求粗这个值,并把这个值放入符合表中

    cout << "1 + 2 + 3 + ... + 10 = " << Sum<10>::VALUE << endl;	//这里只做了访问常量
    cout << "1 + 2 + 3 + ... + 100 = " << Sum<100>::VALUE << endl;
    
    return 0;
}










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值