c++回炉-类

//构造函数
Stock food = Stock("World Cabbage", 250, 1.2);    //显式普通构造函数:写出了函数名;Stock(....)是临时对象,然后复制给了food.
Stock garment("Furry Mason", 50, 5.5);  //隐式普通构造函数:没有函数名
Stock second();    //这是一个函数, 为了和函数区分,隐式调用默认构造函数时候不加()
Stock second;   //对象,隐式调用默认构造函数
Stock second = Stock();   //对象,显式调用默认构造函数
Stock* second = new Stock;   //对象,调用默认构造函数
Stock* second =  new Stock();   //对象,显式调用默认构造函数
//初始化和赋值
Stock stock2 = Stock("xxxx", 2, 2.0);   //是初始化,可能会创建临时对象,也可能不创建。
stock1 = Stock("yyy", 4, 2.5);   //是赋值,Stock(...)创建临时变量,然后赋值,然后删除临时变量

SS  ss = 8;   //SS有一个构造函数,其参数只有一个, 
SS  ss;   ss = 8;   //涉及:用SS(int)创建临时对象,把8作为初始化值,逐成员赋值形式把临时对象的内容复制到ss中。
ss=SS(8);   ss=(SS)8;   //用explicit禁止后,前一个也不能叫转换,可以叫显式调用构造函数赋值。后一个是强制类型转换

const Stock& get_big(const Stock& s) const {
  if (s.data > data)   return s;
  else  return ????;    //这就是this指针的作用,   *this
}

Stock  arr[4];   //调用的默认构造函数
Stock arr[4] = {Stock(), Stock(), Stock(), Stock()};  //显式调用一般构造函数

//enum
enum egg{small, big};		egg mine = big;    //big可能冲突,  sizeof(egg) = 4; 用int来表示enum值
enum class shirt(small, big); 	shirt clth = shirt::small;   //防止其他enum中也有small, 冲突
~~enum class : short shirt{xx, ml};    //xx, ml用short类型实现~~  错了
enum shirt: short {xx, ml};   //sizeof(shirt) = 2

ostream& operator<<(ostream& os, const Time& time){
  os << time.h << time.m << "\n";    return os;
}   //可以级联,  cout<
chaff* p1, *p2;    chaff buff1[30];  chaff buff2[50];
int *p3, *p4;
p1 = new chaff;     p3 = new int[30];
p2 = new (buff1) chaff;  //在buff1的位置分配chaff的内存
p4 = new (buff2) int[20];  //在buff2的位置分配int[20]的内存


char* p = new char;			  delete p;
char* p = new char[len];    delete [] p;
stringbad* pp = new stringbad(bad1);   //用bad1来初始化了新new的stringbad, 利用了拷贝构造函数,如果没有自定义,使用默认的拷贝构造函数
stringbad* pp2 = new stringbad("ddss");  //调用对应构造函数初始化新创建的对象
stringbad* pp3 = new stringbad; //调用默认构造函数

//构造函数中有new动作时候,函数按值传递还是按引用传递都会受影响
struct stringbad {
	stringbad(const char* p);		stringbad();   //必须给一个默认构造函数,构造函数中有new的操作
	~stringbad();		//因为有new,析构函数必须有,且有delete的操作
}
void callme1(stringbad & temp);     void callme2(stringbad temp);     //按引用和按值传递
callme1(obj1);	  //没问题
callme2(obj2);   //出问题,值传递,复制了指针的值即是同一个地址, obj2-复制-temp-函数结束,temp释放,调用析构函数,释放了同一块内存的东西,导致obj2中的数据受损
//对于局部变量,跳出scope时候自动调用析构函数,删除的顺序与创建局部变量的顺序相反
//除了自己定义的构造函数,在参数传递,stringbad te2 = te1;这种情况时候都会调用拷贝构造函数,拷贝构造函数的形参是const stringbad&, 复制构造函数会创建一个副本,也就是stringbad te2 = stringbad(te1)
//参数传递初始化析构了一次,实参在函数结束又析构了一次,两次释放同一块内存导致终止程序运行。

stringbad& string::operator=(const string& other){
  if (this == &other)  return *this;   //this是地址, *this是对象
  delete[]  str;    //如果没有上面的判断,在obj1=obj1完成之前,obj1里面的东西已经空了
  len = other.len;     str=new char[len+1];   std::strcpy(str, other.str);   return *this;
}

struct string{
   string(const char*);
   string(const string&);
   string& operator=(const string&);
};
string te;		char* ch[40];   cin.getline(ch, 40);  
te = ch;    //这时候没有operator=(const char*)   所以采用的是:string(ch)-->te=string(ch)--> ~string(ch), 但是如果重载了operator=(const char*)就不用创建删除临时对象了

类的public函数就是接口。
封装:
1.public接口和实现细节分开,数据作为private,不能直接访问就是封装
2.类函数定义和类声明放在不同的文件中,定义编译成lib不可见也是封装
内联函数应该放到头文件中,被使用内联函数的文件include。
类创建的每个新对象都有自己的存储空间,用于存储内部变量和类成员;同一个类的所有对象共享同一组类方法。
通过成员函数访问数据成员,需要有一个函数负责数据成员的初始化–构造函数。
声明类对象时候自动调用构造函数。
1.显式调用构造函数
2.隐式调用构造函数
构造函数的形式:用于初始化数据成员。一般构造函数,传递参数过去;默认构造函数不传递参数。默认构造函数:1有形参,在形参阶段提供默认值,2没形参,在函数内部提供默认值。 给一般构造函数的参数提供默认值,相当于一举两得。
析构函数用于delete构造函数中new的东西,如果构造函数没有new则析构函数相当于没干事。什么时候写析构函数,看构造函数有没有申请内存空间。
插播:定位newy运算符
头文件: #include

什么时候调用类对象的析构函数?
1.如果是static类型的,程序结束时候自动调用析构函数
2.如果是局部类型的,程序块结束时候调用析构函数
3.如果是new出来的,使用delete时候调用析构函数
4.如果是临时的,使用完后调用析构函数
类对象的赋值, 每个数据成员的内容进行赋值。
const对象意味着所有数据成员都是const,在调用成员函数时候,不能调用非const成员函数,因为可能会改变数据成员。
如果一个类的构造函数只有一个参数,可以用赋值语法创建对象并初始化。这也可以看成是类的默认类型转换。 explcit可以禁止这种特性。也就是explicit作为单个参数的构造函数的前缀。
作用域为类的常量: 枚举,static; 所有对象都不包含枚举,在类作用域中碰到枚举就用枚举值替换。这两个都是因为类没有实例化,不能在类中开辟内存。
友元(友元函数,友元类,友元成员函数)和public函数可以操作private成员变量。
友元函数在类中用friend声明,但不是类的成员函数。不需要::调用也不需要.而是operator+(x,y)这样。
<< 重载必须用友元的原因:cout< 运算符重载函数也是可以重载的,只要标识符不一样(个数,类型,const)
类里面有一种叫转换函数的东西, 也叫运算符函数,用于强制类型转换。因为隐式可能是因为误操作,所以加explicit前缀,使转换函数必须显式调用。
类里面的特殊成员函数:
1.默认构造函数,没有定义任何构造函数时候生效
2.默认析构函数,没有定义析构函数时候生效
3.拷贝构造函数,没有定义拷贝构造函数时候特定调用形式下生效
4.赋值运算符,如果没有重载的话,类会默认生效这个
5.地址运算符,没有定义时候类默认生效
6.移动构造函数
7.移动复制运算符
1.1.默认构造函数构造的对象的数据成员是未知的,只能有一种形式的默认构造函数,如果有多种,编译器不知道匹配哪个报错
2.2.用于初始化时候,不是赋值时候。下面都是初始化操作都会调用拷贝构造函数。
stringbad ditto(mott);
stringbad mee = mott; //可能用拷贝,也可能用赋值
stringbad eg = stringbad(mott); //同上
stringbad* sg = new stringbad(mott)
void fun(stringbad xx); fun(mott);
逐个赋值非静态变量的值,指针就是charp; ditto.p=mott.p; 按值传递; ditto.obj=mott.obj 调用obj的拷贝构造函数;
这也是浅拷贝(默认拷贝构造函数)的弊端,在有初始化操作时候,会多释放一次同一块内存,所以要自己定义拷贝构造函数,初始化时候,重新开辟空间并复制之前的内容到新开辟的空间。这样在初始化使用完后就不会析构掉原始数据的内存了,析构的是被初始化对象自己的内存。
如果一个类包含了用new来初始化的指针对象时候,就要显式定义一个拷贝构造函数。
4.4.默认的赋值运算符原型是:
stringbad& stringbad::operator=(const stringbad&); 接收一个对象引用,返回一个对象引用
赋值和初始化的一个区分就是,初始化是对没有初始值的变量进行的操作,赋值时对已经有初始值的变量进行的操作。比如:
stringbad ob1(“xxxxxx”);
stringbad obj2; obj2 = obj1; //就是赋值
为什么赋值运算符重载时候不能赋值给自身就是上面这个问题,obj1 = obj1,在数据复制给左边的obj1之前,右边的obj1里面的数据就被析构掉了。必须返回引用, 可以级联。代码如上。
c++11的空指针: nullptr. int
p = nullptr.
静态成员函数:
1.不能通过对象调用静态成员函数
2.静态成员函数没有this指针,不与对象关联,所以只能处理静态成员变量,静态成员函数,属于类的成分
重载赋值运算符的好处是节省了临时对象的构建和删除。
构造函数有多个,但是析构函数只有一个,所以不同的构造函数要兼容析构函数。
返回对象,结合函数,一个类对象作为函数的返回值:
1.返回对象时候如果不是引用,调用拷贝构造函数
2.如果返回的引用是来自于形参,则形参是const,返回值也得是const引用
3.按值返回时候(返回局部变量),使用拷贝构造函数生成返回的对象
4.返回指向对象的指针,

你可能感兴趣的:(c++回炉-类)