当前位置:   article > 正文

[C++ 面试基础知识总结] 类

people::people(const string & buf):name(buf) {}

[C++ 面试基础知识总结] 类

参考书籍:《C++ Primer》

目录

类的基础

成员函数

在成员函数中,可以直接访问数据成员,而在这个过程中实际上隐式地使用了一个名为this的隐式指针,该指针指向正是这个类对象。

  1. #include <iostream>
  2. using namespace std;
  3. struct People{
  4. string name = "Summer";
  5. //return name等价于return this->name 或 return (*this).name
  6. string getName() const{return name;};
  7. };
  8. int main(int argc, const char * argv[]) {
  9. const People p{};
  10. cout << p.getName() << endl;
  11. return 0;
  12. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

重点注意:在语句string getName() const{return name;};中,const修饰的是this指针,在这里,this指针的默认类型为People *const,为指向非常量类型的常量指针。在函数后加上const声明,可以将this指针声明为const People *const类型。这里如果不加const声明的话,将会报错,因为类对象p是一个常量。

成员函数也可以在类外部定义,需要加上作用域运算符:

  1. #include <iostream>
  2. using namespace std;
  3. struct People{
  4. string name = "Summer";
  5. string getName() const;
  6. };
  7. string People::getName() const{
  8. return name;
  9. }
  10. int main(int argc, const char * argv[]) {
  11. const People p{};
  12. cout << p.getName() << endl;
  13. return 0;
  14. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

构造函数

如果类没有声明任何构造函数,就会合成默认的构造函数。如果需要默认构造函数,需要定义。

  1. #include <iostream>
  2. using namespace std;
  3. struct People{
  4. string name = "Summer";
  5. int age;
  6. string getName() const{return name;};
  7. };
  8. int main(int argc, const char * argv[]) {
  9. People p;
  10. cout << p.name <<endl; //存在类内初始值,输出"Summer"
  11. cout << p.age << endl; //不存在类内初始值,默认初始化,输出int型的默认初始值0
  12. return 0;
  13. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

一旦定义构造函数,就不会自动生成默认构造函数

  1. #include <iostream>
  2. using namespace std;
  3. struct People{
  4. //默认构造函数
  5. People() = default;
  6. //参数数量不同的构造函数
  7. People(const string &name): name(name){}
  8. People(const string &name, int age): name(name),age(age){}
  9. string name = "Summer";
  10. int age = 26;
  11. string getName() const{return name;};
  12. };
  13. int main(int argc, const char * argv[]) {
  14. People p("7",24);
  15. cout << p.name <<endl;
  16. cout << p.age << endl;
  17. return 0;
  18. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

使用默认构造函数时要注意去掉对象名后的空括号对。

  1. //定义了一个没有形参返回类型为People的函数,而不是对象
  2. People p();
  3. //正确地使用默认构造函数初始化对象
  4. People p;
  • 1
  • 2
  • 3
  • 4

构造函数有一个隐式转换的规则,例如People类定义了一个接受string类型的构造函数,就可以在需要使用People的时候用string代替。

  1. struct People{
  2. //定义接受string类型的构造函数
  3. People(const string &name): name(name),age(20){}
  4. string name ;
  5. int age ;
  6. string getName() const{return name;};
  7. };
  8. void getAge(const People &p){
  9. cout << p.age << endl;
  10. }
  11. int main(int argc, const char * argv[]) {
  12. string s ="Summer";
  13. // 用string替代People
  14. getAge(s);
  15. return 0;
  16. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

但是编译器只会自动地执行一步类型转换。

  1. // 错误,隐式地使用了两种转换
  2. getAge("Summer");
  3. //正确,显示转换为string,再隐式转换为People
  4. getAge(string("Summer"));
  5. //正确,隐示转换为string,再显式转换为People
  6. getAge(People("Summer"));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如果不想通过构造函数,隐式地转换类,可以在构造函数前加上关键字explicit。然后就只能使用强制转换的方式了。

explicit People(const string &name): name(name),age(20){} 
  • 1
  1. // 错误,构造函数是explicit的
  2. getAge(s);
  3. // 正确,两种采用构造函数实行强制转换的方式
  4. getAge(People(s));
  5. getAge(static_cast<People>(s));
  • 1
  • 2
  • 3
  • 4
  • 5

访问控制和封装

在C++中,class和struct唯一区别是,class中成员默认是private的,struct中成员默认是public。

  1. using namespace std;
  2. class People{
  3. public:
  4. string name = "Summer";
  5. string getName() const{return name;};
  6. private:
  7. int age = 26;
  8. };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  1. using namespace std;
  2. class People{
  3. int age = 26;
  4. public:
  5. string name = "Summer";
  6. string getName() const{return name;};
  7. };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  1. using namespace std;
  2. class People{
  3. string name = "Summer";
  4. string getName() const{return name;};
  5. private:
  6. int age = 26;
  7. };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

以上三个类完全一样。

友元

可以通过设置友元的方法来让其他函数访问类的私有成员。

  1. using namespace std;
  2. class People{
  3. //声明函数为People类的友元
  4. friend int getAge(People &p);
  5. public:
  6. string name = "Summer";
  7. string getName() const{return name;};
  8. private:
  9. int age = 26;
  10. };
  11. int getAge(People &p) {
  12. //友元函数可以访问到People类的私有成员
  13. return p.age;
  14. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

也可以设置其他类为类的友元。

  1. using namespace std;
  2. class People{
  3. //声明类Do为People类的友元
  4. friend class Do;
  5. public:
  6. string name = "Summer";
  7. string getName() const{return name;};
  8. private:
  9. int age = 26;
  10. };
  11. class Do{
  12. public:
  13. void setAge();
  14. };
  15. void Do::setAge(){
  16. People p;
  17. //友元类的成员可以访问People类的私有成员
  18. p.age = 20;
  19. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

也可以只让Do的成员函数setAge()成为People类的友元

  1. using namespace std;
  2. //需要在声明友元函数前先声明友元函数所属的类
  3. class Do{
  4. public:
  5. void setAge();
  6. };
  7. class People{
  8. //声明函数为People类的友元
  9. friend void Do::setAge();
  10. public:
  11. string name = "Summer";
  12. string getName() const{return name;};
  13. private:
  14. int age = 26;
  15. };
  16. void Do::setAge(){
  17. People p;
  18. //友元函数可以访问到People类的私有成员
  19. p.age = 20;
  20. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

名字查找与类的作用域

成员函数中使用的名字按照如下方式解析:
1.首先在成员函数内查找该名字的声明,只有在函数使用之前出现的声明才考虑。
2.如果成员函数内没有找到,则在类内继续查找,这时类的所有成员都可以被考虑。
3.如果类内也没有找到该名字的声明,在成员函数定义之前的作用域内查找。

  1. typedef string Type;
  2. Type initVal();
  3. class Test{
  4. public:
  5. typedef double Type;
  6. //这里使用的Type为double
  7. Type setVal(Type);
  8. //要使用外层作用域的Type,需要用::显示指出,这里的前一个Typestring,后一个为double
  9. ::Type setVal2(Type);
  10. //这里使用的Type也为double
  11. Type initVal();
  12. private:
  13. int val;
  14. };
  15. //返回类型出现在函数名之前,返回类型在类的作用域外,需要用::指明所属的类
  16. Test::Type Test::setVal(Type t){
  17. val = t + initVal();
  18. return val;
  19. }
  20. //前一个Type是全局声明,后一个Type在类的作用域内,所以这里都不需要用::
  21. Type Test::setVal2(Type t){
  22. return "";
  23. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

类的静态成员与普通成员

静态成员可以是不完全类型。

  1. //声明一个不完全类型A,只知道A是一个类类型,不知道包含多少成员
  2. class A;
  3. class People{
  4. static A p1; // 正确,静态成员可以是不完全类型
  5. A *p2; // 正确,指针可以是不完全类型
  6. A p3; // 错误,非静态成员必需是完全类型
  7. };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

静态成员可以用作默认实参

  1. class People{
  2. static string name ;
  3. string getName(string n = name ) const{return n;};
  4. };
  • 1
  • 2
  • 3
  • 4
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号