新手需要注意的地方
- 初学者常犯的一种错误是用一个毫不相干的类去派生另一个毫不相干的子类。
- 例如就有人在 Human 类里有一个 swim() 方法,当这哥们在设计一条鱼的时候,就用 Human 去派生一个 Fish 类。
- 从技术角度讲,这麽做没有问题,但像这样的运用继承机制实在太牵强附会了。。。作为一条基本原则,基类和子类之间的关系应该自然和清晰!关于构造器的设计要越简明越好!我们应该只用它来初始化各种有关的属性。
- 作为一个基本原则,在设计、定义和使用一个类的时候,应该让它的每个组成部分简单到不能再简单!
- 最后一点别忘了,析构器的基本用途是对前面所做的事情进行清理。尤其是在使用了动态内存的程序里,析构器将至关重要!
访问控制
在此前的例子里,我们无论是Animal, Pig 和 Turtle 类的所有成员都是用 public: 语句声明。
所谓访问控制,就是C++ 提供了一种用来保护类里的方法和属性的手段。
这里所说的保护意思是对谁可以调用某个方法和访问某个属性加上一个限制。如果某个对象试图调用一个它无权访问的函数,编译器将报错。
我们看下C++中的访问级别:
利用访问级别来保护类里的方法和属性很简单,只要在类里的某个地方写出一个访问级别并在其后加上一个冒号,从那个地方开始往后的所有方法和属性都将受到相应的保护,直到遇到下一个访问级别或者到达这个类的末尾为止!
class Animal { public: std::string name; Animal(std::string theName); void eat(); void sleep(); void drool(); };
请看 name 属性的访问级别是 public,这就意味着任何代码都可以改变它的值。事实我们今后就完全通过 pig.name = “小甲鱼” 来任意改名字了。
我们发觉,如没有任何限制,猪的名字一下子就可以改掉了。这种隐患对这个简单的小程序来说可能没什么大不了的,但如果是发生在一个大型的程序里就足以引发一个逻辑漏洞。 注:BUG无法避免的原因正是因为我们无法模拟各种情况的的输入和修改带来的影响。 就像我们不能随便改变一个人的身份证的名字一样,Animal 类里的 name 属性应该受到保护。 尝试一下:test
1 #include <iostream> 2 #include <string> 3 4 class Animal 5 { 6 public: 7 Animal(std::string theName); 8 void eat(); 9 void sleep(); 10 void drool(); 11 12 protected: 13 std::string name;//对应73行代码 14 }; 15 16 class Pig : public Animal 17 { 18 public: 19 void climb(); 20 Pig(std::string theName); 21 }; 22 23 class Turtle : public Animal 24 { 25 public: 26 void swim(); 27 Turtle(std::string theName); 28 }; 29 30 Animal::Animal(std::string theName) 31 { 32 name = theName; 33 } 34 35 void Animal::eat() 36 { 37 std::cout << "I'm eatting!" << std::endl; 38 } 39 40 void Animal::sleep() 41 { 42 std::cout << "I'm sleeping!Don't disturb me!" << std::endl; 43 } 44 45 void Animal::drool() 46 { 47 std::cout << "我是公的,看到母的我会流口水,我正在流口水。。。" << std::endl; 48 } 49 50 Pig::Pig(std::string theName) : Animal(theName) 51 { 52 } 53 54 void Pig::climb() 55 { 56 std::cout << "我是一个只漂亮的小母猪猪,我会上树,我正在爬树,嘘。。。" << std::endl; 57 } 58 59 Turtle::Turtle(std::string theName) : Animal(theName) 60 { 61 } 62 63 void Turtle::swim() 64 { 65 std::cout << "我是一只小甲鱼,当母猪想抓我的时候,我就游到海里。。哈哈。。" << std::endl; 66 } 67 68 int main() 69 { 70 Pig pig("小猪猪"); 71 Turtle turtle("小甲鱼"); 72 73 pig.name = "小甲鱼";//被保护,无法被修改,见第12,13行代码 74 75 std::cout << "这只猪的名字是: " << pig.name << std::endl; 76 std::cout << "每只乌龟都有个伟大的名字: " << turtle.name << std::endl; 77 78 pig.eat(); 79 turtle.eat(); 80 pig.climb(); 81 turtle.swim(); 82 83 return 0; 84 }
我们需要认识到:
- 一定要记住使用这些访问级别!即时只有你一个人在开发某个项目,全部记住各个类的调用方法也是一件困难的事情。
- 给每个方法和属性加上 protected 或 private 访问级别,就由编译器替你记住那些禁令并在你违反的时候发出警报。
- 使用访问级别对身为程序员的你只有好处,没有坏处!再优秀的程序员也需要这种机械的保护!
- 使用 private 的好处是,今后可以只修改某个类的内部实现,而不必重新修改整个程序。这是因为其他代码根本就访问不到 private 保护的内容,所以不怕”牵一发而动全身”的惨剧发生!
- 在同一个类定义里可以使用多个 public:, private: 和 protected: 语句,但最好把你的元素集中到一个地方,这样代码的可读性会好很多。
- 在编写你的类定义代码时,应该从 public: 开始写起,然后是 protected:, 最后是 private:。
- 虽然编译器并不挑剔这些顺序,但这么做的好处是:当你想知道某个特定的类提供了哪些方法和属性时,好的顺序可以为你节省大量的时间!
请思考: class Pig : public Animal { … } 是什么意思?
关于从基类继承来的方法和属性的保护: class Pig : public Animal { … }
C++ 不仅允许你对在类里定义的方法和属性实施访问控制,还允许你控制子类可以访问基类里的哪些方法和属性。
public 是在告诉编译器:继承的方法和属性的访问级别不发生任何改变 – 即 public 仍可以被所有代码访问,protected 只能由基类的子类访问,privat 则只能由基类本身访问。
protected 把基类的访问级别改为 protected , 如果原来是 public 的话。这将使得这个子类外部的代码无法通过子类去访问基类中的 public 。
private 是在告诉编译器从基类继承来的每一个成员都当成 private 来对待,这意味着只有这个子类可以使用它从基类继承来的元素。
乱?!烦?!不怕,一般都只用 public 而已!