c++面向对象编程入门(一)

作用域关键字:

private:(私有的)  自身--friend修身友好类

protected:(友好的) 子类--自身--friend修身友好类

pubilc:(公共的) 能看到的地方  类变量都行

类:在类中实现的函数都是默认inline,默认类型 private

class student

{

student(){}无参数构造函数

student(int a,char *objname):arg(a),name(objname)//初始化列表,能提高效率const对象、引用对象必须使用初始化列表

{

//函数体

}

~student(){}//析构函数


void function1(void)

{

//inline函数

}

void function2(void);

pubilc:

int arg;

char *name;

const int grade;//const  初始化时必须要在析构函数的初始化列表中进行,不能再函数中

}

voidstudent::function2(void)

{

//一般类函数

}

构造函数要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载。

(1)为什么构造函数不能有返回值

·如果它们有返回值,要么编译器必须知道如何处理返回值,要么就只能由客户程序员自己来显式的调用构造函数析构函数,这样一来,安全性就被人破坏了.另外,析构函数不带任何参数,因为析构不需任何选项.
如果允许构造函数有返回值,在某此情况下,会引起歧义。返回不知道给谁

(2)为什么构造函数不能为虚函数

·虚函数调用的机制,是知道接口而不知道其准确对象类型的函数,但是创建一个对象,必须知道对象的准确类型;当一个构造函数被调用时,它做的首要事情之一就是初始化它的VPTR来指向VTABLE。

(3)当构造函数是单参数时加上explicit修饰,单参数的构造函数,可能存在隐式转换的问题,因为单参数构造函数,和拷贝构造函数形式类似,调用时很可能会发生隐式转换,应加上explicit关键字

explicitstudent(int a):arg(a){}

(4) 对于初始化列表而言,对成员变量的初始化,是严格按照声明次序,而不是在初始化列表中的顺序进行初始化

(5)C++构造函数初始化按下列顺序被调用:
·首先,任何虚拟基类的构造函数按照它们被继承的顺序构造;
·其次,任何非虚拟基类的构造函数按照它们被继承的顺序构造;
·最后,任何成员对象的构造函数按照它们声明的顺序调用;


#include <iostream>
using namespace std;
class OBJ1{
public:
OBJ1(){ cout<<"OBJ1\n"; }
};
class OBJ2{
public:
OBJ2(){ cout<<"OBJ2\n";}
}
class Base1{
public:
Base1(){ cout<<"Base1\n";}
}
class Base2{
public:
Base2(){ cout <<"Base2\n"; }
};
class Base3{
public:
Base3(){ cout <<"Base3\n"; }
};
class Base4{
public:
Base4(){ cout <<"Base4\n"; }
};
class Derived :public Base1, virtual public Base2,public Base3, virtual public Base4//继承顺序{
public:
Derived() :Base4(), Base3(), Base2(),Base1(), obj2(), obj1(){//初始化列表
cout <<"Derived ok.\n";
}
protected:
OBJ1 obj1;//声明顺序
OBJ2 obj2;
};

int main()
{
Derived aa;//初始化
cout <<"This is ok.\n";
return 0;
}
结果:
Base2 //虚拟基类按照被继承顺序初始化
Base4 //虚拟基类按照被继承的顺序 
Base1 //非虚拟基类按照被继承的顺序初始化
Base3 //非虚拟基类按照被继承的顺序 
OBJ1 //成员函数按照声明的顺序初始化
OBJ2 //成员函数按照声明的顺序 
Derived ok. 
This is ok.

析构函数

赋值函数

拷贝构造函数

拷贝构造函数和赋值构造函数的异同
由于并非所有的对象都会使用拷贝构造函数赋值函数,程序员可能对这两个函数有些轻视。请先记住以下的警告,在阅读正文时就会多心:如果不主动编写拷贝构造函数赋值函数,编译器将以“位拷贝”的方式自动生成缺省的函数。倘若类中含有指针变量,那么这两个缺省的函数就隐含了错误。以类String 的两个对象a,b 为例,假设a.m_data 的内容为“hello”,b.m_data 的内容为“world”。现将a 赋给b,缺省赋值函数的“位拷贝”意味着执行b.m_data = a.m_data。这将造成三个错误:一是b.m_data 原有的内存没被释放,造成内存泄露;二是b.m_data 和a.m_data 指向同一块内存,a 或b 任何一方变动都会影响另一方;三是在对象被析构时,m_data 被释放了两次。拷贝构造函数赋值函数非常容易混淆,常导致错写、错用。拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。



类String 的拷贝构造函数赋值函数
  // 拷贝构造函数
  String::String(const String &other)//这里必须是引用,否则会无限循环
  {

//拷贝构造函数可理解为类的拷贝但是重新开辟了空间,用已有的对象初始化新类对象

  // 允许操作other 的私有成员m_data
  int length = strlen(other.m_data);
  m_data = new char[length+1];
  strcpy(m_data, other.m_data);
  }
  // 赋值函数
  String & String::operator =(const String &other) 
  {

//重载修饰符operator     格式 (const String &other)  返回值为类的引用
  // (1) 检查自赋值
  if(this == &other)
  return *this;
  // (2) 释放原有的内存资源
  delete [] m_data;
  // (3)分配新的内存资源,并复制内容
  int length = strlen(other.m_data);
  m_data = new char[length+1];
  strcpy(m_data, other.m_data);
  // (4)返回本对象的引用
  return *this;
  }

下面那些为拷贝构造函数那些为赋值函数

String a(“hello”);
  String b(“world”);
  String c = a; // 调用了拷贝构造函数,最好写成 c(a);
  c = b; // 调用了赋值函数
  本例中第三个语句的风格较差,宜改写成String c(a) 以区别于第四个语句。

 类String 拷贝构造函数与普通构造函数的区别是:在函数入口处无需与NULL 进行比较,这是因为“引用”不可能是NULL,而“指针”可以为NULL。类String 的赋值函数构造函数复杂得多,分四步实现:
  (1)第一步,检查自赋值。你可能会认为多此一举,难道有人会愚蠢到写出 a = a 这样的自赋值语句!的确不会。但是间接的自赋值仍有可能出现,例如
  // 内容自赋值
  b = a;
  …
  c = b;
  …
  a = c;
  // 地址自赋值
  b = &a;
  …
  a = *b;
  也许有人会说:“即使出现自赋值,我也可以不理睬,大不了化点时间让对象复制自己而已,反正不会出错!”他真的说错了。看看第二步的delete,自杀后还能复制自己吗?所以,如果发现自赋值,应该马上终止函数。注意不要将检查自赋值的if 语句
  if(this == &other)
  错写成为
  if( *this == other)
  (2)第二步,用delete 释放原有的内存资源。如果现在不释放,以后就没机会了,将造成内存泄露。
  (3)第三步,分配新的内存资源,并复制字符串。注意函数strlen 返回的是有效字符串长度,不包含结束符‘\0’。函数strcpy 则连‘\0’一起复制。
  (4)第四步,返回本对象的引用,目的是为了实现象 a = b = c 这样的链式表达。注意不要将 return *this 错写成 return this 。那么能否写成return other 呢?效果不是一样吗?不可以!因为我们不知道参数other 的生命期。有可能other 是个临时对象,在赋值结束后它马上消失,那么return other 返回的将是垃圾。
  偷懒的办法处理拷贝构造函数赋值函数
  如果我们实在不想编写拷贝构造函数赋值函数,又不允许别人使用编译器生成的缺省函数,怎么办?
  偷懒的办法是:只需将拷贝构造函数赋值函数声明为私有函数,不用编写代码。
  例如:
  class A
  { …
  private:
  A(const A &a); // 私有的拷贝构造函数
  A & operator =(const A &a); // 私有的赋值函数
  };
  如果有人试图编写如下程序:
  A b(a); // 调用了私有的拷贝构造函数
  b = a; // 调用了私有的赋值函数
  编译器将指出错误,因为外界不可以操作A 的私有函数。


1、  拷贝构造函数首先是一个构造函数,它调用的时候产生一个对象,是通过参数传进来的那个对象来初始化,产生的对象。  
   operator=();是把一个对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检查一下两个对象是不是同一个对象,如果是的话就不做任何操作。

2、还要注意的是拷贝构造函数构造函数,不返回值   
   而赋值函数需要返回一个对象自身的引用,以便赋值之后的操作  


http://www.niftyadmin.cn/n/1193341.html

相关文章

Ecshop布局参考图

文章列表页: article_cat.dwt 文章内容页: article.dwt商品品牌页: brand.dwt所有分类页: catalog.dwt商品列表页: category.dwt商品比较页: compare.dwt购物车和购物流程页: flow.dwt商品相册页: gallery.dwt商品详情页: goods.dwt团购商品详情页: group_buy_goods.dwt团购商品…

c++面向对象编程(二)重载

operator--关键字 前缀加 i Fraction& operator() //前缀自加重载&#xff1b;&#xff08;前置版本prefix&#xff09; { numden; //先自增&#xff0c;再返回&#xff1b; return *this; } 后缀加 i cons…

laravel auth认证

在laravel 可以使用 auth 门脸类来认证 如果你使用的不是框架自带的认证&#xff0c;即可按下面的方式来做 当登陆页面用户填写完信息提交&#xff0c;在控制器中 use Illuminate\Support\Facades\Auth 使用了auth后即可使用Auth::attempt(数组)来认证&#xff0c;attempt的密码…

c++面向对象编程(三)--多态

多态&#xff1a;接口的多种不同的实现方式即为多态&#xff0c;同一操作作用于不同的对象&#xff0c;可以有不同的解释&#xff0c;产生不同的执行结果。在运行时&#xff0c;可以通过指向基类的指针&#xff0c;来调用实现派生类中的方法。 C中&#xff0c;实现多态有以下方…

Core Image 制作自己的美图秀秀

一、Core Image是和啥&#xff1f; 关于这个问题不太好回答&#xff0c;但其实又也很简单。肯定是苹果推出的关于图片处理的API。网上各路大神都有解释过&#xff0c;不过能看明白的不多&#xff0c;我的理解就是Photoshop的滤镜&#xff0c;当然在各种美图软件里都有这个功能&…

更新pip10后 ImportError: cannot import name ‘main'

百度了几个回答都没有解决问题&#xff0c;有些回答明显是直接复制过来的一点价值都没有&#xff0c;然后google一下立马解决。很多时候不能怪搜索引擎&#xff0c;问题出在一些国内网友对知识的不负责任 解决&#xff1a;找到报错文件&#xff0c;也就是那个pip&#xff0c;然…

c++面向对象编程(四)--模板

一、函数模板( Function templates) 1、 模板(Templates)使得我们可以生成通用的函数&#xff0c;这些函数能够接受任意数据类型的参 数&#xff0c;可返回任意类型的值&#xff0c;而不需要对所有可能的数据类型进行函数重载。这在一定 程度上实现了宏&#xff08;macro&…

【BZOJ】【3437】小P的牧场

DP/斜率优化 斜率优化基本题……等等&#xff0c;好像就没啥变化啊 嗯目测这题跟仓库建设差不多&#xff1f;写题的时候倒是没想这么多……直接推了公式。 $$f[i]min\{f[j]cal(j,i)a[i]\}$$ 哦麻烦的还是这个$cal(j,i)$ 我们令$s[i]\sum_{k1}^{i}b[k], c[i]\sum_{k1}^{i}(b[k]…