[c++] 面试题之犄角旮旯 第壹章

记录C/C++语言相关的问题。html

算法可视化:https://visualgo.net/enios

<data structure and algorithm in c++> By Adam 有免费电子版 c++

 

 

1. while( ) 与 Ctrl+D 

Ctrl+D后,默认仍是要执行完后面的程序。程序员

void multi_input(void)
{
    int sum = 0;
    int value;
    while(std::cin >> value)
    {
        cout << "-" << endl;
        sum += value;
    }
    cout << sum << endl;
}

multi_input()

multi_input()

第一个multi_input()执行中,执行Ctrl+D后:算法

一旦测试失败,while 终止并退出循环体,执行 while 以后的语句数组

该语句在输出 sum 后输出 endl,endl 输出换行并刷新与 cout 相关联的缓冲区。安全

最后,执行 return,一般返回零表示程序成功运行完毕。curl

 

2. c++英语学习

声明 Declarations
定义 Definitions

小括号 parenthesis [pə'renθɪsɪs] 中括号 square brackets 花括号 curly brace

实参
argument
形参 parameter

操纵符 manipulator
...

 

3. 赋值越界

字符

C++ 中,把负值赋给 unsigned 对象是彻底合法的,其结果是该负数对该类型的取值个数求模后的值ide

因此,若是把 -1 赋给 8 位的 unsigned char, 那么结果是 255, 由于 255 是 -1 对256 求模后的值。函数

 

数字的最大值

float 型只能保证 6位有效数字,可能不够用;

double 型至少能够保证 10位有效数字 知足大部分计算的须要。 

size of types

#include<limits>
cout << numeric_limits<double>::max() << endl;

 

字符串的不一样类型

若是链接普通字符串字面值和宽字符串字面值,将会出现什么结果呢?例如:

// Concatenating plain and wide character strings is undefined
std::cout << "multi-line " L"literal " << std::endl;

其结果是未定义的,也就是说,链接不一样类型的行为标准没有定义。

这个程序可能会执行,也可能会崩溃或者产生没有用的值,并且在不一样的编译器下程序的动做可能不一样。

wchar_t 

L'a' stored in wchar_t.

 

4. 数据间的转化

unsigned int a = 1;
signed   int b = -1;  // “局部”大范围 --> 小范围
cout << a*b << endl;

4294967295
-1 is promoted to unsigned int, but the binary representation stays
the same: 111111111111111111111111111 (32 bits)
The unsigned value is: 4294967295, therefore
4294967295 * 1 = 4294967295

auto 类型

自动去匹配可能的类型,有解的话就不会报错。

 

5. 运算符的优先级(precedence)

int i = 2;
int j = (i = 3) * i;
cout << "j = " << j << endl;

j = 9;   // 由于括号优先级高

const

  The primary role of const is to specify immutability.

const int a = 100;
const long &r = a;
// Can refer to an object of a different (but related) type. 但仍是不要这么作了吧,容易误会。

Jeff: const int val; int const val 目前编译器解释是同样的。

int i = 0;
int *const p1 = &i; // we can't change the value of p1; const is top-level
const int ci = 42; // we cannot change ci; const is top-level

const int *p2 = &ci; // we can change p2; const is low-level 指向的只是一个特定的类型 const int *const p3 = p2; // right-most const is top-level, left-most is not const int &r = ci; // const in reference type is always low-level 引用的只是一个特定的类型

 

6. Function

Lvalue vs Rvalue

  • rvalue: the content in the vector   -- 内容/值 只会出如今RHS
  • lvalue: address of the vector      -- RHS, LHS均可能出现。

"传值"的英文叫法是:Call by value or Call by reference.

Return value

Make sure that do not return local object. 

 

注意:Low-level是能够区分函数的定义的哦:)

 

7. Namespace

reference: http://www.jizhuomi.com/software/289.html

命名空间: 本质上讲namespace是对全局做用域的细分。

      将多个变量和函数等包含在内,使其不会与命名空间外的任何变量和函数等发生重命名的冲突。

 1 #include <iostream>  
 2 using namespace std;  
 3   
 4 // 命名空间JiZhuoMi  
 5 namespace JiZhuoMi  
 6 {  
 7     char *szUrl = "www.jizhuomi.com";  
 8 }  
 9   
10 // 命名空间Software  
11 namespace Software  
12 {  
13     char *szUrl = "www.jizhuomi.com/software/";  
14 }  
15   
16 // 释放命名空间JiZhuoMi和Software  
17 using namespace JiZhuoMi;  
18 using namespace Software;  
19   
20 int _tmain(int argc, _TCHAR* argv[])  
21 {  
22     char *szUrl = "url";  
23     cout << szUrl << endl;  
24     return 0;  
25 } 

 

8. How to print

Reference: http://www.cplusplus.com/reference/ios/ios_base/fmtflags/

具备默认输出格式

cout 输出的数字的有效数字位数有默认的限制:6个有效数字。

 1     cout << "[" << 1 << "]" << endl;  2     std::cout << 1331 << std::endl;  3     cout << "[" << 2 << "]" << endl;  4     std::cout << "In hex " << std::hex << 1331 << std::endl;  5     cout << "[" << 3 << "]" << endl;  6     std::cout << 1331.123456 << std::endl;   // 只保留了俩位小数,看来默认是6个有效数字  7     cout << "[" << 4 << "]" << endl;  8  std::cout.setf(std::ios::scientific, std::ios::floatfield);  // 设置了cout的属性为科学计数法  9     cout << "[" << 5 << "]" << endl; 10     std::cout << 1331.123456 << std::endl; 11     cout << "[" << 6 << "]" << endl; 12     std::cout << std::setprecision(3) << 1331.123456 << std::endl; // 进一步设置,科学技术法的小数点 13     cout << "[" << 7 << "]" << endl; 14     std::cout << std::dec << 1331 << std::endl;   // 设置回了十进制 15     cout << "[" << 8 << "]" << endl; 16     std::cout.width(8);  // 下一次 ONLY 写[八]个words. 17     cout << "[" << 9 << "]" << endl; 18     std::cout << 1331 << std::endl; 19     cout << "[" << 10 << "]" << endl; 20  std::cout.setf(std::ios::left, std::ios::adjustfield); 21     cout << "[" << 11 << "]" << endl; 22     std::cout.width(8);  // 自动对齐的一种技巧 23     cout << "[" << 12 << "]" << endl; 24     std::cout << 1331 << std::endl; 25     cout << "[" << 13 << "]" << endl;

16, 设置下次输出的一个下限。

 

自动对齐输出

15-22, 自动对齐的技巧。

[1]
1331
[2]
In hex 533
[3]
1331.12
[4]
[5]
1.331123e+03
[6]
1.331e+03
[7]
1331
[8]
       [9]
1331
[10]
[11]
[       12]
1331
[13]
Result

 

9. cin的陷阱

【1】cin.getline(str, len):缓冲区有残留

当缓冲区中有残留数据时,cin函数会直接取得这些残留数据而不会请求键盘输入。

#include <iostream>
using namespace std;
int main()
{
    char str[8];
    cin.getline(str, 5); // input多少,这里都只从缓冲区 读取5个。剩下的就自动成为了下一次输入。
    cout<<str<<endl;
    cin.getline(str, 5);
    cout<<str<<endl;
    return 0;
}

 

【2】cin >>, 空格会成为终止

#include <iostream>
using namespace std;
int main()
{
   char str1[10], str2[10];
   cin>>str1;  // 输入:abcd efg. 第一次读取字符串时遇到空格则中止.
   cin>>str2;  // 缓冲区有残留数据,读入操做直接从缓冲区中取数据。
   cout<<str1<<endl;
   cout<<str2<<endl;
   return 0;
}

看来,比较稳妥的方式是:使用cin.getline(255)这种便可。

 

【3】cin.get(<char>)

空格不会成为终止(空格可能就是要输入的字符*_*),Enter能够。

#include <iostream>
using namespace std;
int main()
{
  char c1, c2;
   cin.get(c1);
   cin.get(c2);
    cout<<c1<<" "<<c2<<endl; // 打印两个字符
    cout<<(int)c1<<" "<<(int)c2<<endl; // 打印这两个字符的ASCII值
    return 0;
}

自定义 终止符如何?

#include <iostream>
using namespace std;
int main ()
{
  char ch, a[20];
  cin.get(a, 5 , 'd'); //设置d为终止符
  cin>>ch;
  cout<<a<<endl;
  cout<<(int)ch<<endl;
  return 0;
}

注意:识别结束符,但不丢弃。

 

【4】灵活清空缓冲区

为何ch的输出错了呢?

#include <iostream>
using namespace std;
int main ()
{
  char ch, a[20];
  cin.getline(a, 5);  // 输入123456, a: 1234
  cin>>ch;        // ch: 0, but not the ascii of '5'
  cout<<a<<endl;
  cout<<(int)ch<<endl;
  return 0;
} 

因此,输入一次数据后,最好记得清空“input buffer"。灵活的清空方法以下:

cin.ignore(numeric_limits<std::streamsize>::max(),’\n’);//清除输入缓冲区的当前行 
cin.ignore(numeric_limits<std::streamsize>::max()); //清除输入缓冲区里全部内容 
cin.ignore()//清除一个字符

 

10. 智能指针 - 类型转换

reference: http://www.jellythink.com/archives/205

C++中的类型转换分为两种:

    1. 隐式类型转换;
    2. 显式类型转换。(这里的重点)

在标准C++中有四个类型转换符:static_cast、dynamic_cast、const_cast和reinterpret_cast;

static_cast & dynamic_cast

执行非多态类型的转换,用于代替C中一般的转换操做

double myDouble = 3.14; int cast1 = (int)myDouble; // c-style
int cast2 = int(myDouble); int cast3 = static_cast<int>(myDouble); // recommended
  1. 用于类层次结构中,基类和子类之间指针和引用的转换;
    当进行上行转换,也就是把子类的指针或引用转换成父类表示,这种转换是安全的;【类比:结构体 --> 子结构体】
    当进行下行转换,也就是把父类的指针或引用转换成子类表示,这种转换是不安全的,也须要程序员来保证;【类比:子结构体 --> 外层结构体】
    • dynamic_cast主要用于类层次间的上行转换和下行转换,还能够用于类之间的交叉转换。【多继承的状况】
    • 在类层次间进行上行转换时,dynamic_cast和static_cast的效果是同样的;
    • 在进行下行转换时,dynamic_cast具备类型检查的功能,比static_cast更安全。(详见:暂未定关于类的篇章)
  2. 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum等等,这种转换的安全性须要程序员来保证;(上述例子)
  3. 把void指针转换成目标类型的指针,是极其不安全的;

More details: C++多继承动态交叉转换dynamic_cast

 

reinterpret_cast

C++里的强制类型转换符。让指针value改变时更安全一些。

// Returns a hash code based on an address
unsigned short hash(void *p) {   unsigned long val = reinterpret_cast<unsigned long>(p);   return ( unsigned short )(val ˆ (val >> 16)); }

当开辟了系统全局的内存空间,须要在多个应用程序之间使用时,须要彼此共享,传递这个内存空间的指针时,就能够将指针转换成整数值,获得之后,再将整数值转换成指针,进行对应的操做。

 

const_cast

用于修改类型的const或volatile属性。

#include <iostream>
using namespace std;
class CA
{
public:
     CA():m_iA(10){}
     int m_iA;
};
int main() { const CA *pA = new CA; // pA->m_iA = 100; // Error CA *pB = const_cast<CA *>(pA); pB->m_iA = 100;  // 临时性能够修改了
// Now the pA and the pB points to the same object cout<<pA->m_iA<<endl; cout<<pB->m_iA<<endl; const CA &a = *pA; // a.m_iA = 200; // Error CA &b = const_cast<CA &>(a); b.m_iA = 200; // Now the a and the b reference to the same object cout<<b.m_iA<<endl; cout<<a.m_iA<<endl; }

通常不要用! 以上例子,本来的const指针无法修改对象的变量,但const_cast以后的新指针,即可以了。但,这样的操做要慎重!

 

11. 数组

多维数组

打开多维array的正确方式:

    • 体会auto的魅力。
    • begin与end的技巧。
    int ia[3][4] = { 
        1, 2, 3, 4,
        5, 6, 7, 8,
        9, 10, 11, 12
    };  

    for (auto p = std::begin(ia); p != std::end(ia); ++p) {
        for (auto q = std::begin(*p); q != std::end(*p); ++q) {
            std::cout << *q << " ";
        }
        std::cout << std::endl;
    }

string的遍历:避免告终束符问题,提倡!

string str = "lolop";
for (auto &c : str) { c = toupper(c); } cout << str << endl;

 

矩阵:数值计算的数组

c++11面向数值计算的数组:valarray

数值计算专列:使用正确的类型,处理的正确问题,也方便代码阅读。

    int days[10] = {31, 28, 30, 30, 31, 30, 31, 30, 31, 30};
    valarray<int> vi; 
    valarray<double> di(9);
    valarray<float> fi(3.14, 10);
    valarray<int> vid(days, 5); 
    vid *= 10; 
    cout << vid[3] << endl;

 

动态数组

记得delete就好。

    int n = 10; 
    int *dyn_arr = new int [n];
    for ( int i = 0; i < n; i++)
    {   
        dyn_arr[i] = i;
    }   

    delete [] dyn_arr;

  

12. Operator Overload

1. 在重载下标运算符时(数组符号):不可重载为友元函数,必须是非static类的成员函数。 why?
 
/* implement */
 
 
2. overload ++ 时,若是是: int a; ++a; ++在前面时,怎么办?
答:counter counter::operator ++(int)
  

3. 关于虚析构函数,问:基类中的析构函数通常都设置为virtual,是么?不设置的话,会出现什么问题?

用C++开发的时候,用来作基类的类的析构函数通常都是虚函数。
总的来讲虚析构函数是为了不内存泄露,并且是当子类中会有指针成员变量时才会使用获得的。
也就说虚析构函数使得在删除指向子类对象的基类指针时能够调用子类的析构函数达到释放子类中堆内存的目的,而防止内存泄露的。
 
 

 
 “消息传递须要消耗时间“,缘由是?
 

 
 
 
 
 
 
暂时放在这里? 本篇须要大总结和整理.
 

Macros should never be required in C++.
Variables should be defined close to their use.
Don’t use malloc/realloc, use new (or smart pointers)
Minimise use of void*, pointer arithmetic, union and c-style casts
Minimise the use of C-style arrays and strings, use vector/array (from C++11) and string instead.

总之,忘掉C语言。

相关文章
相关标签/搜索