当前位置:   article > 正文

C++学习笔记3--作用域 构造析构器 访问控制 友元关系_小甲鱼的友元

小甲鱼的友元

作用域

我们发觉作用域解析操作符(::),作用是告诉编译器这个方法存在于何处,或者说是属于哪一个类。

其实我们对这个不应该感到陌生,从一个开始小甲鱼就不提倡using namespace std;这样偷懒的做法,所以我们是std::cout。。。

事实上std::cout所引用的是std里定义的cout,而std::string数据类型其实也是一个对象。

C++允许在类里声明常量,但不允许对它进行赋值

class Car

{

public:

const float TANKSIZE = 85; // 出错@_@

}

 

绕开这一限制的方法就是创建一个静态常量

class Car

{

public:

static const float FULL_GAS = 85;

}

一般不会这么做。。

类似于使用结构的情况,可以在声明某个类的同时立刻创建一些该类的对象:

class Car

{

。。。。。。

} car1, car2;

这种做法在C++里是允许的。但作为一种良好的编程习惯,应该避免这种做法!

Car car1, car2;

car1.setColor(“WHITE”);

。。。。。。

car2 = car1;

把一个对象赋值给另一个同类的对象将会自动使同名的属性有同样的值

构造器和析构器

构造器和通常方法的主要区别:

构造器的名字必须和它所在的类的名字一样

系统在创建某个类的实例时会第一时间自动调用这个类的构造器

构造器永远不会返回任何值

 创建构造器,需要先把它的声明添加到类里:

class Car

{

Car( void );

}

 

注意大小写与类名保持一致。在结束声明之后开始定义构造器本身:

Car::Car(void)    // 不用写 void Car::Car(void)

{

color = “WHITE”;

engine = “V8”;

wheel = 4;

gas_tank = FULL_GAS;

}

类名第一个字母大写

构造对象数组

之前我们已经说过,数组可以是任何一种数据类型,当然也包括对象。

如:Car mycar[10];

调用语法依旧是:mycar[x].running;

注:x代表着给定数组元素的下标。

一种常见的做法是在创建对象的同时做一些事情(构造器背后搞鬼),在对象创建出来之后用另一个方法做同样或者不同的事情。

如:

Car mycar;

mycar.setColor(“Yellow”);

 

构造器和析构器两者相辅相成,有许多共同之处。首先,析构器有着和构造器/类一样的名字,只不过前边多了一个波浪符“~”前缀。

class Car

{

Car(void);

~Car();

}

其次,析构器也永远不返回任何值。

另外,析构器是不带参数的。所以析构器的声明永远是如下格式:~ClassName();

在我们刚刚的例子中析构器可有可无。但是在比较复杂的类里,析构器往往至关重要(可能引起内存泄露)。

例如某个类的构造器申请了一块内存,我们就必须在析构器里释放那块内存。

this指针和类的继承

class Human 
{
    char  fishc;
    Human(char  fishc);
}
Human::Human(char  fishc)
{
    fishc = fishc;
}

 

我们看到,在”fishc = fishc”之前,所有的语法都没有任何问题:

Human()构造器有一个名为fishc的参数

虽然他与Human类里边的属性同名,但却是不相干的两样东西,所以并没有错。

 

可是,问题是怎样才能让构造器知道哪个是参数,哪个是属性呢?

这时候,就需要用到他了 – this指针。

this -> fishc = fishc

 

现在编译器就懂了,赋值操作符的左边将被解释为当前对象的fishc属性,右边将被解释为构造器的传入来的fishc参数。

注意:

使用this指针的基本原则是:如果代码不存在二义性隐患,就不必使用this指针!

继承的语法

class SubClass : public SuperClass { … }

class Pig : public Animal { … }

 

子类的函数的写法

void Pig :: climb()

{

  .....................

}

继承机制中的构造器和析构器

比如基类有个构造器,如Animal(),它将在创造 Pig 类型的对象时最先被调用,如果 Pig 类也有一个构造器,它将排在第二个被调用。

因为基类必须在子类之前初始化原则!

然后我们继续讨论:如果构造器带着输入参数,事情变得稍微复杂了。

class Animal

{

public:

Animal( std::string theName );

std::string name;

}

class Pig : public Animal

{

public:

Pig( std::string theName );

}

 

那么我们的方法应该如何定义呢?

 

Animal::Animal( std::string theName )

{

name = theName;

}

Pig::Pig( std::string theName ) : Animal( theName )

{

}

 

注意在子类的构造器定义里的”:Animal(theName)”语法含义是:

当调用 Pig() 构造器时(以 theName 作为输入参数),Animal()构造器也将被调用( theName 输入参数将传递给它)。

于是,当我们调用 Pig pig(“小猪猪”); 将把字符串 “小猪猪” 传递给 Pig() 和 Animal(),赋值动作将实际发生在 Animal() 方法里。

在销毁某个对象时,基类的析构器也将被自动调用,但这些事情编译器会自动替你处理。

因为析构器不需要输入参数,所以根本用不着使用 :SuperClassMethod(arguments) 语法!

与构造器的情况相反,基类的析构器将在子类的最后一条语句执行完毕后才被调用。

 

#include <iostream>
#include <string>

class BasseClass
{
public:
 BasseClass();
 ~BasseClass();

 void doSomething();
};

class SubClass : public BasseClass
{
public:
 SubClass();
 ~SubClass();
};

BasseClass::BasseClass()
{
 std::cout << "进入基类构造器......\n";
 std::cout << "我在基类构造器里边干了某些事.......\n\n";
}

BasseClass::~BasseClass()
{
 std::cout << "进入基类析构器......\n";
 std::cout << "我在析构器里边也干了某些事....\n\n";
}

void BasseClass::doSomething()
{
 std::cout << "我干了某些事.....\n\n";
}

SubClass::SubClass()
{
 std::cout << "进入子类构造器.......\n";
 std::cout << "我在子类构造器里边还干了某些事.....\n\n";
}

SubClass::~SubClass()
{
 std::cout << "进入子类析构器......\n";
}

int main()
{
 SubClass subclass;
 subclass.doSomething();
 std::cout << "完事,收工!\n";
 
 return 0;
}

结果

进入基类构造器......
我在基类构造器里边干了某些事.......

进入子类构造器.......
我在子类构造器里边还干了某些事.....

我干了某些事.....

完事,收工!
进入子类析构器......
进入基类析构器......
我在析构器里边也干了某些事....

Press any key to continue

访问控制

我们看下C++中的访问级别:

 

利用访问级别来保护类里的方法和属性很简单,只要在类里的某个地方写出一个访问级别并在其后加上一个冒号。

从那个地方开始往后的所有方法和属性都将受到相应的保护,直到遇到下一个访问级别或者到达这个类的末尾为止!

覆盖方法和重载方法

关于从基类继承来的方法和属性的保护:

class Pig : public Animal { … }

C++ 不仅允许你对在类里定义的方法和属性实施访问控制,还允许你控制子类可以访问基类里的哪些方法和属性。

public

是在告诉编译器:继承的方法和属性的访问级别不发生任何改变 – 即 public 仍可以被所有代码访问,protected 只能由基类的子类访问,privat 则只能由基类本身访问。

protected

把基类的访问级别改为 protected , 如果原来是 public 的话。这将使得这个子类外部的代码无法通过子类去访问基类中的 public 。

 

private

是在告诉编译器从基类继承来的每一个成员都当成 private 来对待,这意味着只有这个子类可以使用它从基类继承来的元素。

方法覆盖和重载

当我们需要在基类里提供一个通用的函数,但在它的某个子类里需要修改这个方法的实现,在 C++ 里,覆盖(overriding)就可以做到。

回到我们之前的例子,我们都知道,但凡是个动物都知道吃!那么吃我们就可以说是动物的一个共同特征,但我们知道不同动物会有不同的吃法。。。。。。

 

C++ 可以让我们很容易实现这种既有共同特征又需要在不同的类里有不同实现的方法。

我们需要做的是在类里重新声明这个方法,然后再改写一下它的实现代码(就像它是一个增加的方法那样)就行啦。

注意:

Animal::eat();//不必要定义对象才调用eat。。只要前面声明就可以了

重载和C差不多

在对方法进行覆盖(注意区分覆盖和重载)时一定要看仔细,因为只要声明的输入参数和返回值与原来的不一致,你编写出来的就将是一个重载

方法而不是覆盖方法。这种错误以小甲鱼的个人经验告诉你:往往很难调试!

对从基类继承来的方法进行重载,程序永远不会像你预期的那样工作,子类不能对基类的方法进行重载。。重载必须在同一类中

友元关系

声明一个友元关系的语法很简单,只要在类声明里的某个地方加上一条 friend class ** 就行了。

注:这条语句可以放在任何地方,放在 public, protected, private 段落里都可以。

 

#include <iostream>
#include <string>

class Lovers
{
public:
 Lovers(std::string theName);
 void kiss(Lovers *lover);
 void ask(Lovers *lover, std::string something);

protected:
 std::string name;

 friend class Others;
};

class Boyfriend : public Lovers
{
public:
 Boyfriend(std::string theName);
};

class Girlfriend : public Lovers
{
public:
 Girlfriend(std::string theName);
};

class Others
{
public:
 Others(std::string theName);
 void kiss(Lovers *lover);

protected:
 std::string name;
};

Lovers::Lovers(std::string theName)
{
 name = theName;
}

void Lovers::kiss(Lovers *lover)//要修改lovers类中的name必须传个指针
{
 std::cout << name << "亲亲我们家的" << lover->name << std::endl;
}

void Lovers::ask(Lovers *lover, std::string something)
{
 std::cout << "宝贝儿" << lover->name << "帮我" << something << std::endl;
}

Boyfriend::Boyfriend(std::string theName) : Lovers(theName)
{
}

Girlfriend::Girlfriend(std::string theName) : Lovers(theName)
{
}

Others::Others(std::string theName)
{
 name = theName;
}

void Others::kiss(Lovers *lover)
{
 std::cout << name << "亲一下" << lover->name << std::endl;
}

int main()
{
 Boyfriend boyfriend("A君");//可能是利用构造函数初始化,构造函数可以重载的呀就是N多参数
 Girlfriend girlfriend("B妞");

 Others others("路人甲");

 girlfriend.kiss(&boyfriend);
 girlfriend.ask(&boyfriend, "洗衣服啦");

 std::cout << "\n当当当当!传说中的路人甲登场。。。。。。\n";
 others.kiss(&girlfriend);//因为other是lover的友元类所以能访问到他的protected下的name

 return 0;
}

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/51708
推荐阅读
相关标签
  

闽ICP备14008679号