您的位置:首页 > 娱乐 > 八卦 > 安卓app开发实验报告_输入文字生成图片app_公司推广方法有哪些_病毒式营销案例

安卓app开发实验报告_输入文字生成图片app_公司推广方法有哪些_病毒式营销案例

2024/10/5 20:17:01 来源:https://blog.csdn.net/weixin_70873145/article/details/142357324  浏览:    关键词:安卓app开发实验报告_输入文字生成图片app_公司推广方法有哪些_病毒式营销案例
安卓app开发实验报告_输入文字生成图片app_公司推广方法有哪些_病毒式营销案例

 在最开始如果有些,看不懂可以去看上一篇 -->类和对象 上

阅读时要结合代码一起思考

 学习完日期类可以看看以下oj题

KY111 日期差值

计算一年的第几天

1. 类的默认成员函数

  1. 默认成员函数,编译器会自动生成的成员函数被成为默认成员函数,一个类默认不写的情况下编译器会默认生成一下6个默认成员函数,最重要的是前4个,取地址重载不是很重要,了解即可。默认成员函数比较复杂,但是很重要;在开始之前需要提出两个问题
    1. 不写时,编译器默认生成的函数行为时什么,是否满足我们的需求?
    2. 编译器不满足我们的需求,那么要怎么实现?

2. 构造函数

构造函数是特殊的成员函数,构造函数的任务不是开辟空间创建对象(经常使用的局部对象是函数栈帧创建的,空间就开辟好了),构造函数的作用是在对象实例化的时初始化对象用的,构造函数的本质和Init函数功能一样,构造函数自动调用的特点就完美的替代了Init(初始化函数)

  1. 构造函数的特点:
    1. 函数名与类名相同;对象实例化后就会自动调用构造函数
    2. 无返回值;在类里写构造函数时,不用写返回值(也不用写void,C++规定的)
    3. 对象实例化的时候系统会自动调用对应的构造函数;可以通过函数重载找到对应函数
    4. 函数可以重载;但是要注意的是无参和全缺省函数不可以同时出现,语法上没问题,但是调用会出问题
    5. 如果类中没有显示定义构造函数,则C++编译器会自动生成一个无参的构造函数(大部分情况下编译器生成的构造函数,是达不到我们想要的效果的),如果自己写了编译器就不会自己生成
    6. 无参构造函数、全缺省构造函数、不写构造函数时编译器默认生成的构造函数,这些都是默认构造函数;但是这三个函数有且只有一个存在,不能同时存在;总的来说就是不传实参就可以调用的构造就是默认构造
    7. 不写构造函数,编译器默认生成的构造,对内置类型成员变量的初始化没有要求(有可能不会处理),初不初始化看编译器;自定义类型成员变量要调用成员变量的默认构造函数初始化才行,没有默认构造函数就会报错,要初始化成员变量就需要用到初始化列表才行 初始化列表比较复杂,后面再说
#include<iostream>
using namespace std;
class Date
{
public:// 1.无参构造函数//Date()//{//	_year = 1;//	_month = 1;//	_day = 1;//}2.带参构造函数//Date(int year, int month, int day)//{//	_year = year;//	_month = month;//	_day = day;//}//3.全缺省构造函数Date(int year = 1, int month = 1, int day = 1)// 既可以传无参,也可以带参,还可以部分传参{_year = year;_month = month;_day = day;}void Print(){cout << _year << '-' << _month << '-' << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{//Date d1();//调用无参的后面不要接括号,因为可能会被误以为是函数声明Date d1;d1.Print();//Date d2(2000,3,1);//即代表对象实例化,也会去调用默认构造函数,如果要传参直接有对应值//d2.Print();Date d3(2222);d3.Print();return 0;
}
  1. 全缺省默认构造函数,既满足无参的,也满足带参的
  2. 无参和全缺省的函数不能同时出现,函数重载没问题,但是调用函数传递无参的时候,系统调用那个呢?全缺省的也可以不传参调用
  3. 注意:写全缺省参数,只需要写在声明的地方即可

  1. 这种情况有资源申请的需要自己写构造,什么情况不需要自己写呢?
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申请空间失败");return;}_capacity = n;_top = 0;}
private:STDataType* _a;size_t _capacity;size_t _top;
};
int main()
{//有了默认构造函数Stack s1;return 0;
}
  1. 编译器自动生成的构造,两个栈模拟队列,这种就不需要自己写
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申请空间失败");return;}_capacity = n;_top = 0;}
private:STDataType* _a;size_t _capacity;size_t _top;
};
class Myqueue
{
public://
private:Stack s1;Stack s2;int size;//编译器可能没有初始化,并没有处理内置类型
};
int main()
{//有了默认构造函数//Stack s1;//编译器自动生成的构造Myqueue m;return 0;
}
  1. 总结:大多数情况下都是需要自己写构造函数的,只有少数情况像Myqueue、Stack有默认构造才不需要写;这里的Myqueue自动生成就可用了
  2. 所以大多数的情况:应该写就都要写

有资源申请的一般都要写,构造/析构/复制拷贝/拷贝

3. 析构函数

析构函数与构造函数相反,C++规定对象在销毁的时候会自动调用析构函数,完成对象中的资源释放,比如局部的对象是存放在栈帧里面的,函数结束局部对象就会跟着销毁,所以我们不用管;他和Stack的Destroy函数的功能类似,也是用来销毁空间的;如果没有malloc开辟的空间是不需要资源释放的,所以按理来说上面写的Date是不需要析构函数的

  1. 析构函数的特点:
    1. 要使用析构函数,在类名字的前面加上 ~ 这个符号,就是析构函数了
    2. 单个局部域有多个对象,C++规定后定义的先析构
    3. 和构造函数类似,如果不写析构,编译器自动生成的析构函数对内置类型的成员不做处理;自定义类型会调用自己的析构函数
    4. 一个类只能有一个析构函数,如果没有显示写出析构函数,那么系统会自动生成默认的析构函
    5. 对象生命周期结束前会自动调用析构函数
    6. 无参数无返回值。(也和构造函数一样不用加void)
    7. 如果没有资源申请(例:malloc动态开辟的),析构函数可以不写,直接使用编译器自动生成的析构函数即可,如果有资源申请,就一定要写析构函数,否则会导致资源泄漏;
    8. 我们显示写析构函数,对于自定义类型成员还是会调用自己的析构函数,最后自定义类型成员无论什么情况都会自动调用析构函数(可以自行观察~Myqueue()析构函数的变化)
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申请空间失败");return;}_capacity = n;_top = 0;}// Stack的析构,如果不写就会导致内存泄漏~Stack(){free(_a);_capacity = _top = 0;}
private:STDataType* _a;size_t _capacity;size_t _top;
};
class Myqueue
{
public://自己写了析构函数,但是没有做处理;此时为了防止内存泄漏编译器还是会去调用自动生成的析构函数~Myqueue(){cout << "^Myqueue" << endl;}private:Stack s1;Stack s2;//int size;//编译器可能没有初始化,并没有处理内置类型
};
int main()
{//有了默认构造函数//Stack s1;//编译器自动生成的构造Myqueue m;return 0;
}

4. 拷贝构造函数

  1. 一个构造函数如果第一个参数是自身类 类型的引用,此时就是拷贝构造函数,除此以外,后面也可以接其他的参数,这个也是拷贝构造函数;拷贝构造是一个特殊的构造函数
  2. 个人理解:拷贝构造,创建好了一个对象,用已经存在的对象给创造的对象初始化,本质上还是构造函数
  3. 拷贝构造的特点:
    1. 拷贝构造函数是构造函数的一个重载
    2. 拷贝构造函数只有一个并且必须是类 类型对象的引用;如果使用传值传参会在语法上引发无穷递归,编译器会直接报错

C++规定传值传参会调用拷贝构造

#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){this->_year = year;this->_month = month;this->_day = day;}//拷贝构造Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << this->_year << '/' << this->_month << '/' << this->_day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2024,7,31);d1.Print();//拷贝构造,创建好了一个对象,用已经存在的对象给创造的对象初始化Date dd(d1);//也是拷贝构造Date dd2 = d1;dd.Print();return 0;
}

  1. C++规定自定义的类型对象进行拷贝必须调用拷贝构造,自定义类型传值传参和传值返回都会调用拷贝构造完成(下一章会详细说明)
  2. 如果没有显示定义拷贝构造,编译器会自动生成一个拷贝构造函数自动生成的拷贝构造对内置类型会完成值拷贝或者浅拷贝(一个一个字节拷贝、和memcpy类似)对于自定义类型的成员变量会调用它的拷贝构造 主要就是看类的成员变量 是否是 内置还是自定义
  3. Date这样的类成员变量都是内置类型没有指向资源,编译器自动生成的拷贝构造就可以完成拷贝的需要,所以不需要自己显示写拷贝构造。Stack有资源申请,编译自动生成的拷贝构造(值拷贝或者浅拷贝)不够用了,所以此时需要显示写拷贝构造而且得是深拷贝构造;当然也有需要不要的,比如例子中的myqueue有着天然的优势,自动生成的拷贝构造就够用了,里面的stack会自动调用自己的拷贝构造
    1. 指向同一块空间,你改变了也会影响我
    2. 对于有资源申请的,浅拷贝会析构两次
#include <iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申请空间失败");return;}_capacity = n;_top = 0;}// st2(st1)Stack(const Stack& st){cout << "Stack(const Stack& st)" << endl;// 需要对_a指向资源创建同样大的资源再拷贝值_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);if (nullptr == _a){perror("malloc申请空间失败!!!");return;}memcpy(_a, st._a, sizeof(STDataType) * st._top);_top = st._top;_capacity = st._capacity;}void Push(STDataType x){if (_top == _capacity){int newcapacity = _capacity * 2;STDataType* tmp = (STDataType*)realloc(_a, newcapacity *sizeof(STDataType));if (tmp == NULL){perror("realloc fail");return;}_a = tmp;_capacity = newcapacity;}_a[_top++] = x;}~Stack(){cout << "~Stack()" << endl;free(_a);_a = nullptr;_top = _capacity = 0;}
private:STDataType* _a;size_t _capacity;size_t _top;
};class myqueue
{Stack t1;Stack t2;
};
int main()
{Stack s1;s1.Push(20);s1.Push(10);//假设此时还没有写赋值拷贝构造,此时调用默认的拷贝构造,是浅拷贝,把地址也拷贝过来了就会导致两个对象指向同一块空,而且还会出现两次析构,会导致程序崩溃Stack s2(s1);s2 = s1;// 这个也是赋值拷贝构造//myqueue有着天然优势,不需要自己写拷贝构造,编译器自己生成的就能用myqueue my;myqueue my2(my);return 0;
}
  • 传值返回会产生临时对象并调用拷贝构造,传值引用返回:不会产生拷贝,是返回一个对象的别名(引用);如果此时返回的对象是一个存在局部域内的对象,函数结束返回时会调用析构函数空间会被回收,此时返回去的就是一个野引用和C语言的野指针差不多,如果要使用引用返回应该避免这种问题;只有在返回对象时出函数保证对象不被销毁,此时才可以使用引用返回

//一般情况下参数传参都用引用,除非有特殊需要
//传值返回的时候会调用拷贝构造
Stack fun2(Stack& s)
{s.Push(10);s.Push(20);return s;
}//引用返回不会去调用拷贝构造,相当于返回别名了
//Stack& fun2(Stack& s)
//{
//	s.Push(10);
//	s.Push(20);
//	return s;
//}// 这个对象在局部域,会导致返回对象别名消失,返回了一个野引用
Stack& fun3()
{Stack ten1;return ten1;
}int main()
{Stack s1;Stack ret = fun2(s1);Stack ret2 = fun3();return 0;
}

5.赋值运算符重载

5.1. 运算符重载

  1. 可以发现除了编译器自带的加、减、乘、除、大于、小于、等于,这些常用的运算符编译器等等,都可完成运算;但是比较年月日的呢?或者说这个食品还有多久过期这些;编译器自带的就不够用了,所以有了运算符重载
  2. 运算符被用于类 类型的对象时,C++语言让我们通过运算符重载的形式来给定义新的含义(就是这个运算符不再是原本的意思)。C++规定类 类型对象使用运算符时,必须将d1 == d2,转换成调用对应的运算符重载 operator==(d1,d2),如果没有对应的运算符重载会编译报
    1. 注:d1和d2,都是下方例子中的对象
  3. 运算符重载是具有特殊名字的函数,它的名字由operator和后面要定义的运算符共同构成;和其他函数一样,它具有返回类型参数列表以及函数体
  4. 重载运算符函数的参数个数和这个运算符作用的运算对象个数一样多,一元运算符( -- 、++ 等等)有一个参数二元运算符有两个参数,二元运算符左边计算对象传递给第一个参数,右边计算对象传递给第二个参数
  5. 如果重载运算符函数是一个成员函数,则它是一个运算对象默认传递隐式的this指针,所以参数比运算对象少一个
#include<iostream>
using namespace std;
class Date
{
public:Date(int year = 2, int month = 2, int day = 2)//构造函数和类同名{_year = year;_month = month;_day = day;}void Print(){cout << _year << '/' << _month << '/' << _day <<endl;}//方法2:间接访问int GetYear(){return _year;}int GetMonth(){return _month;}int GetDay(){return _day;}//方法4,这样就只需要传递一个参数,有隐含的this指针bool operator==(Date d2){return _year == d2._year&& _month == d2._month&& _day == d2._day;}
//方法1,放开权限 //private:
private:int _year;int _month;int _day;
};//3.一般情况下,成员变量是不希望直接被访问的,所以有两个方法,第一:间接访问、第二:运算符重载放到类里面
//bool operator==(Date d1, Date d2)
//{
//	return d1.GetYear() == d2.GetYear()
//		&& d1.GetMonth() == d2.GetMonth()
//		&& d1.GetDay() == d2.GetDay();
//}int main()
{//实例化类两个对象,还调用了构造函数Date d1(2024,7,29);Date d2(2024,7,29);//重载为全局时,要考虑如何访问类的私有成员//1.放开类的私有限定//2.在类里面提供一个get函数获取私有对象//3.友元函数,后面再说//4.重载为成员函数,推荐第4种方法// 方法1//operator==(d1,d2);//d1 == d2;//方法4,这里d1表示d1里面的运算符重载,而调用类里面的函数时默认传递一个this指针,第二个参数是d2d1.operator==(d2);//运算符重载if (d1 == d2){cout << "Yes" << endl;}else{cout << "No" << endl;}return 0;
}

需要注意的是使用第4种方法只要,传递一个参数,因为有一个隐含的this指针

如果同时出现两个运算符重载,一个在类里面另外一个是全局,会优先调用类里面

  • 运算符重载后,优先级和结合性还是和内置类型运算符保持一致
    1. 优先级:运算的先后顺序
    2. 结合性:当运算符两侧的优先级一样时,结合性决定了运算对象与运算符的结合顺序;意思就是优先级相同是从左向右开始算,还是从右向左开始算
    3. 假设: a + b - c 此时是从右向左,那么就会先算 b - c然后再加上a
  • 不能使用语法中没有的符号来创建新的操作符:比如operator@。
  • ( .*    ::    sizeof   ?: (三目)    . ) 注意这5个运算符不能重载 ;看下面举例 .* 符号的使用方法
#include <iostream>
using namespace std;
class V
{
public:void fun(){cout << "V::fun()" << endl;}
private:};
void print()
{cout << "print()" << endl;
}
int main()
{// V::fun ,类域访问限定符,访问类域里面的函数;V.fun 是调用这个函数typedef void (V::*vf)();//成员函数指针,重命名为vf ,意思是将这个函数类型重命名typedef void (*vp)();vf vget = nullptr;//函数指针vget = &V::fun;//C++规定需要用取地址符才能拿到函数地址V va;// 拿到这个类域里面函数的地址,可是在类域里面所以用到了(va.*vget)();这个方法也是回调函数的用法(va.*vget)();                 //如果是全局通过函数指针的方式调用是不用 va. 的vp vprint = &print;(*vprint)();return 0;
}
  1. 重载操作符最少有一个类型是类 类型参数,不可以通过运算符重载来改变内置类型的原本意思,例如:operator+(int n,int m);就是错的,两个参数都是内置类型了
  2. 一个类需要重载哪些运算符,要看你重载的运算符有没有意义,比如Date类的运算符operator+,可以计算多少天后的哪一个,perator* 对于日期类就没意义了

5.2. 赋值运算符重载

赋值运算符重载是一个默认成员函数,用来完成两个已经存在的对象直接的拷贝赋值;这里需要区分拷贝构造,拷贝构造:一个同类型的(旧)对象初始化给另一个要创建的对象

  1. 赋值运算符重载是一个运算符重载,规定必须重载为成员函数,建议写成const 当前类 类型的引用,不然会调用拷贝构造
  2. 也有返回值,不过更加推荐写成类 类型的引用,可以减少拷贝,提高效率;有返回值的目的为了支持连续赋值

  1. 没有显示写的时候,编译器会自动生成一个赋值运算符重载,和默认拷贝构造函数类似,对内置成员类型进行值拷贝(浅拷贝),对自定义类型调用自己对应的赋值重载函数
  2. 前面说过对域stack这样的类,虽然也是内置类型,但是有了资源申请,编译器自动生成的浅拷贝达不到要求,所以也需要自己实现深拷贝;像之前的Myqueue这样的类型,编译器自动生成的就够用;
  3. 如果显示写了析构函数,那么就需要写赋值运算符重载,反之不需要
#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){this->_year = year;this->_month = month;this->_day = day;}//拷贝构造Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}// dd2 = d2 赋值重载拷贝void operator=(Date d){_year = d._year;_month = d._month;_day = d._day;}//建议写成 带引用的方式,可以减少拷贝void operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;}void Print(){cout << this->_year << '/' << this->_month << '/' << this->_day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2024, 7, 31);Date d2(1999, 12, 21);d1.Print();//拷贝构造,创建好了一个对象,用已经存在的对象给创造的对象初始化Date dd(d1);//也是拷贝构造Date dd2 = d1;// 赋值重载dd2 = d2;return 0;
}

#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1){this->_year = year;this->_month = month;this->_day = day;}//拷贝构造Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}// dd2 = d2 赋值重载拷贝//void operator=(Date d) 拷贝构造一份给了d,然后在赋值拷贝给d1Date& operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;//解引用返回,对this一级指针;返回dd2的别名}void Print(){cout << this->_year << '/' << this->_month << '/' << this->_day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2024, 7, 31);Date d2(1999, 12, 21);d1.Print();//拷贝构造,创建好了一个对象,用已经存在的对象给创造的对象初始化Date dd(d1);//也是拷贝构造Date dd2 = d1;// 赋值重载dd2.operator=(d2);dd2 = d1 = d2;d1 = d2 ;return 0;
}

5.3. 日期类实现

此篇内容较多,所以单独分离一篇  ---->日期类

6. 取地址运算符重载

6.1. const成员函数

  1. const修饰的成员函数称为const成员函数,放到成员函数参数列表后面
  2. const是用来修饰该成员函数的this指针的,在函数里面不能修改类里的任何成员

const如果修饰指针本身是不存在权限放大缩小的问题的

const在 * 号左边,表示指向的内容不能修改,权限缩小,如果此时传递过来的this指针去修改就会导致权限放大的问题

传递实参的类型需要和形参一一对应的

  1. Print隐含的this指针从 Date* const this --> const Date* const this
class A
{
public:A(int a){_a = a;}void Print() const{cout << _a << endl;}
private:int _a;//int _b;
};
int main()
{const A aa2(10);aa2.Print();//权限可以缩小 但是不能放大A aa3(30);aa3.Print();return 0;
}

6.2.取地址运算符重载

  1. 取地址运算重载,一般分为普通取地址运算符重载和const取地址运算符重载,一般编译器自动生成的就够用了,不需要自己写;除非有特殊情况
class Date
{
public:Date* operator&(){return this;// return nullptr;}const Date* operator&()const   // const Date* const this{return this;// return nullptr;//可以使坏,返回一个 (Date*)0x03aff48}
private:int _year; // 年int _month; // ⽉int _day; // ⽇
};

加油啊!!! 兄弟们  为了美好的明天而战

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com