《Beginning C++17》-学习笔记-Chapter 08-Defining Functions

/*A function with a return type other than void must return a value of the type specified in the function header. The only exception to this rule is the main() function, where reaching the closing brace is equivalent to returning 0. */

/*If the return type is specified as void, no expression can appear in a return statement. It must be written simply as follows:
return;
If the last statement in a function body executes so that the closing brace is reached, this is equivalent to executing a return statement with no expression. In a function with a return type other than void, this is an error, and the function will not compile—except for main(), of course.*/

/*You can declare a function before you use or define it by means of a function prototype. This way you don't have to put the defination of a function before the position where this function is called.*/

/*It could be a good idea to always write prototypes for each function that is defined in a source file—with the exception of main(), of course, which never requires a prototype. Specifying prototypes near the start of the file removes the possibility of compiler errors arising from functions not being sequenced appropriately. It also allows other programmers to get an overview of the functionality of your code.*/

/*With the pass-by-value mechanism, the values of variables or constants you specify as arguments are not passed to a function at all. Instead, copies of the arguments are created, and these copies are transferred to the function. */

/*When a function parameter is a pointer type, the pass-by-value mechanism operates just as before. However,
a pointer contains the address of another variable; a copy of the pointer contains the same address and
therefore points to the same variable.*/
// Modifying the value of a caller variable
#include <iostream>

double increase_by_ten(double*);   // Function prototype

int main()
{
	double it{ 5.0 };
	increase_by_ten(&it);

	std::cout << "After function execution, it = " << it << std::endl;
}


double increase_by_ten(double* pointer_to_variable)
{
	*pointer_to_variable += 10.0;
	return  *pointer_to_variable;
}
// Passing an array to a function
#include <iostream>
#include <array>          // For std::size()

double average(double array[], size_t count);    // Function prototype
/*The above function prototype can also be defined as follows, which is considered identical by the compiler*/
//double average(double* array, size_t count);

int main()
{
	double values[]{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 };
	std::cout << "Average = " << average(values, std::size(values)) << std::endl;
}

// Function to compute an average
//The elements of the array that is passed to average() are accessed using normal array notation.
//double average(double array[], size_t count)
//{
//	double sum{};                                  // Accumulate total in here
//	for (size_t i{}; i < count; ++i)
//		sum += array[i];                             // Sum array elements
//	return sum / count;                            // Return average
//}
/* you can also treat an array passed to a function as a pointer and use pointer notation to access the
elements.Here’s how average() would look in that case:*/

//double average(double* array, size_t count)
//{
//	double sum{}; // Accumulate total in here
//	for (size_t i{}; i < count; ++i)
//		sum += *array++; // Sum array elements
//	return sum / count; // Return average
//}
/*both  array and pointer notations are completely equivalent.
you can freely mix both notations. For instance, use array notation with a pointer parameter: */
double average(double* array, size_t count)
{
	double sum{}; // Accumulate total in here
	for (size_t i{}; i < count; ++i)
		sum += array[i]; // Sum array elements
	return sum / count; // Return average
}

#include <iostream>


/*const double* and double* const differ in a way that in the former the value is const while in the other the pointer is const.*/

int main()
{
	double double_num1{ 123 };
	double double_num2{ 456 };
	const double * ptr_to_const_double{ &double_num1 };//pointer to constant double

	//*ptr_to_const_double_var += 5;//Erro List: expression must be a modifiable lvalue;
	//						//'const_double-var':you cannot assign to a variable that is const
	ptr_to_const_double = &double_num2;
	std::cout << *ptr_to_const_double << std::endl;

	double * const const_ptr_to_double{ &double_num1 };//constant pointer to double
	//const_ptr_to_double = &double_num2;//Erro List: expression must be a modifiable lvalue;
	////						//'double_const_var':you cannot assign to a variable that is const
	*const_ptr_to_double += 1;
	std::cout << *const_ptr_to_double << std::endl;


}
// Passing a two-dimensional array to a function
#include <iostream>
#include <array>	          // For std::size()

double yield(const double values[][4], size_t n);

int main()
{
	double beans[3][4]{
	  { 1.0, 2.0, 3.0, 4.0},
	  { 5.0, 6.0, 7.0, 8.0 },
	  { 9.0, 10.0, 11.0, 12.0 }
	};
	std::cout << std::size(beans) << std::endl;//output 3
	std::cout << "Yield = " << yield(beans, std::size(beans)) << std::endl;
}

// Function to compute total yield
double yield(const double array[][4], size_t size)
{
	double sum{};
	for (size_t i{}; i < size; ++i)        // Loop through rows
	{
		for (size_t j{}; j < 4; ++j)         // Loop through elements in a row
		{
			sum += array[i][j];// same as "sum += *(*(array + i) + j);"
		}
	}
	return sum;
}
// Modifying the value of a caller variable – references vs pointers
#include <iostream>

void change_it_by_pointer(double* reference_to_it);    // Pass pointer (by value)
void change_it_by_reference(double& reference_to_it);  // Pass by reference

int main()
{
	double it{ 5.0 };

	change_it_by_pointer(&it);                     // Now we pass the address
	std::cout << "After first function execution, it = " << it << std::endl;

	change_it_by_reference(it);                    // Now we pass a reference, not the value!
	std::cout << "After second function execution, it = " << it << std::endl;
}

void change_it_by_pointer(double* pit)
{
	*pit += 10.0;                // This modifies the original double
}
void change_it_by_reference(double& pit)
{
	pit += 10.0;                 // This modifies the original double as well!
}

/*calling a function that has a reference parameter is syntactically
indistinguishable from calling a function where the argument is passed by value. This makes it particularly
important to use a reference-to-const parameter in a function that does not change the argument. */


// Using a reference parameter
#include <iostream>
#include <iomanip>
#include <string> 
#include <vector> 

using std::string;
using std::vector;

void find_words(vector<string>& words, const string& str, const string& separators);
void list_words(const vector<string>& words);

int main()
{
	string text;                                               // The string to be searched
	std::cout << "Enter some text terminated by *:\n";
	std::getline(std::cin, text, '*');

	const string separators{ " ,;:.\"!?'\n" };                 // Word delimiters
	vector<string> words;                                      // Words found

	find_words(words, text, separators);
	list_words(words);
}

void find_words(vector<string>& words, const string& str, const string& separators)
{
	size_t start{ str.find_first_not_of(separators) };        // First word start index
	size_t end{};                                             // Index for end of a word

	while (start != string::npos)                             // Find the words
	{
		end = str.find_first_of(separators, start + 1);         // Find end of  word
		if (end == string::npos)                                // Found a separator?
			end = str.length();                                   // No, so set to last + 1

		words.push_back(str.substr(start, end - start));        // Store the word
		start = str.find_first_not_of(separators, end + 1);     // Find 1st character of next word
	}
}
/*The first parameter of find_words() is a reference, which avoids copying the vector<string> object.
More important, though, it is a reference to a non-const vector<>, which allows us to add values to the vector
from inside the function. Such a parameter is therefore sometimes called an output parameter because it is
used to collect a function’s output. Parameters whose values are purely used as input are then called input
parameters.
The find_words() function does not modify the values passed to the second and third parameters.
Both are, in other words, input parameters and should therefore never be passed by reference-to-nonconst.
Reference-to-non-const parameters should be reserved for those cases where you need to modify
the original value—in other words, for output parameters.
You can pass a non-const argument to a reference-to-const parameter but never the other way around.*/


void list_words(const vector<string>& words)
{
	std::cout << "Your string contains the following " << words.size() << " words:\n";
	size_t count{};                                           // Number output
	for (const auto& word : words)
	{
		std::cout << std::setw(15) << word;
		if (!(++count % 5))
			std::cout << std::endl;
	}
	std::cout << std::endl;
}
#include <iostream>
#include <array>          // For std::size()

double average10(const double(&array)[10]);


int main()
{
	double values[]{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 };
	std::cout << "Average = " << average10(values) << std::endl;
}

double average10(const double(&array)[10]) /* Only arrays of length 10 can be passed! */
{
	double sum{}; // Accumulate total in here
	for (size_t i{}; i < 10; ++i)
		sum += array[i]; // Sum array elements
	return sum / 10; // Return average
}

/*The extra parentheses surrounding &array are required,
though. Without them, the compiler would no longer interpret the parameter type as a reference to an array
of doubles but as an array of references to double. Because arrays of references are not allowed in C++, this
would then result in a compiler error:*/

/* if you pass a fixed-size array by reference, it can be used as input to operations
such as sizeof(), std::size(), and range-based for loops.以下函数功能同上面的函数一样,只是在两处分别用for loop 和 size() 代替了10 */
//double average10(const double(&array)[10])
//{
//	double sum{}; // Accumulate total in here
//	for (double val : array)
//		sum += val; // Sum array elements
//	return sum / std::size(array); // Return average
//}
// Passing an array to a function - use std::array<>
#include <iostream>
#include <array>

double average10(const std::array<double, 10>& array);        // Function prototype

int main()
{
	std::array<double, 10> values{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 };
	// std::array<double,3> values{ 1.0, 2.0, 3.0 };           // Only three values!!!
	std::cout << "Average = " << average10(values) << std::endl;
}

// Function to compute an average
double average10(const std::array<double, 10>& array)
{
	double sum{};                       // Accumulate total in here
	for (double val : array)
		sum += val;                        // Sum array elements
	return sum / array.size();           // Return average
}
// Using string_view parameters
#include <iostream>
#include <iomanip>
#include <string> 
#include <string_view> 
#include <vector> 

using std::string;
using std::vector;

void find_words(vector<string>& words, std::string_view str, std::string_view separators);
void list_words(const vector<string>& words);

int main()
{
	string text;                                               // The string to be searched
	std::cout << "Enter some text terminated by *:\n";
	std::getline(std::cin, text, '*');

	const string separators{ " ,;:.\"!?'\n" };                 // Word delimiters
	vector<string> words;                                      // Words found

	find_words(words, text, separators);
	list_words(words);
}

void find_words(vector<string>& words, std::string_view str, std::string_view separators)
{
	size_t start{ str.find_first_not_of(separators) };        // First word start index
	size_t end{};                                             // Index for end of a word

	while (start != string::npos)                             // Find the words
	{
		end = str.find_first_of(separators, start + 1);         // Find end of  word
		if (end == string::npos)                                // Found a separator?
			end = str.length();                                   // No, so set to last + 1

		words.push_back(std::string{ str.substr(start, end - start) });        // Store the word

		//std::string_view objects have to be converted to std::string object explicitly.
		start = str.find_first_not_of(separators, end + 1);     // Find 1st character of next word
	}
}

void list_words(const vector<string>& words)
{
	std::cout << "Your string contains the following " << words.size() << " words:\n";
	size_t count{};                                           // Number output
	for (const auto& word : words)
	{
		std::cout << std::setw(15) << word;
		if (!(++count % 5))
			std::cout << std::endl;
	}
	std::cout << std::endl;
}
#include <iostream>
#include <string_view>
void show_error(std::string_view message)
{
	std::cout << message << std::endl;
}

void show_error(std::string_view message = "Default Program Error");//Default Argument Values

int main()
{
	show_error();
	show_error("Customized Program Error");
}
// Using multiple default parameter values
#include <iostream>
#include <iomanip>
#include <string_view>
/*All function parameters that have default values must be placed together at the end of the parameter list.
When an argument is omitted in a function call, all subsequent arguments in the list must also be omitted.
Thus, parameters with default values should be sequenced from the least likely to be omitted to the most
likely at the end.*/
// The function prototype including defaults for parameters
void show_data(const int data[], size_t count = 1, std::string_view title = "Data Values",
	size_t width = 10, size_t perLine = 5);

int main()
{
	int samples[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

	int dataItem{ -99 };
	show_data(&dataItem);

	dataItem = 13;
	show_data(&dataItem, 1, "Unlucky for some!");

	show_data(samples, sizeof(samples) / sizeof(samples[0]));
	show_data(samples, sizeof(samples) / sizeof(samples[0]), "Samples");
	show_data(samples, sizeof(samples) / sizeof(samples[0]), "Samples", 6);
	show_data(samples, sizeof(samples) / sizeof(samples[0]), "Samples", 8, 4);
}

void show_data(const int data[], size_t count, std::string_view title, size_t width, size_t perLine)
{
	std::cout << title << std::endl;                      // Display the title

	// Output the data values
	for (size_t i{}; i < count; ++i)
	{
		std::cout << std::setw(width) << data[i];           // Display a data item
		if ((i + 1) % perLine == 0)                         // Newline after perLine values
			std::cout << '\n';
	}
	std::cout << std::endl;
}
#include <iostream>

int main()
{

	int dataItem{ -99 };
	int * data{ &dataItem };

	std::cout << *data << std::endl;
	std::cout << data[0] << std::endl;
}
// Using multiple default parameter values
#include <iostream>

int  main(int argc, char*  argv[])
{
	for (int i{}; i < argc; ++i)
		std::cout << argv[i] << std::endl;
}
/*The first parameter, argc, is a count of the number of string arguments that were found on the
command line. The second parameter, argv, is an array of pointers to the command-line arguments,
including the program name. The array type implies that all command-line arguments are received as
C-style strings. The program name used to invoke the program is normally recorded in the first element of argv, argv[0].*/
#include <iostream>
#include <string>
#include <string_view>
// auto deduced to type string
auto getFirstLetter(std::string_view text) // function to get first letter,
{ // not as a char but as another string
	if (text.empty())
		return " "; // deduced type: const char*
	else
		return text.substr(0, 1).data(); // deduced type: std::string_view
}


int main()
{
	std::cout << getFirstLetter(" ");

}
#include <iostream>
#include <string>
#include <string_view>
// auto deduced to type string_view
auto getFirstLetter(std::string_view text) // function to get first letter,
{ // not as a char but as another string
	if (text.empty())
		return std::string_view{ " " }; // deduced type: const char*
	else
		return text.substr(0, 1); // deduced type: std::string_view
}


int main()
{
	std::cout << getFirstLetter(" ");

}
/*auto never deduces to a reference type, always to a value type. This implies that even when
you assign a reference to auto, the value still gets copied. This copy will moreover not be const, unless you
explicitly use const auto. To have the compiler deduce a reference type, you can use auto& or const auto&.*/

#include <iostream>
#include <string>

int main()
{
	std::string test = "Your powers of deduction never cease to amaze me";
	const std::string& ref_to_test = test;
	auto auto_test = ref_to_test;
	std::cout << auto_test << std::endl;
	/*auto_test has type std::string and therefore contains a copy of test.
Unlike ref_to_test, this new copy isn’t const anymore either.*/
	auto_test = "CONTENT CHANGED.";
	std::cout << auto_test << std::endl;

}
/*Note that while this syntax makes optional <> objects look and feel like pointers, they most certainly aren’t
pointers. Each optional<> object contains a copy of any value assigned to it, and this copy is not kept in the
free store. That is, while copying a pointer doesn’t copy the value it points to, copying an optional<> always
involves copying the entire value that is stored inside it.*/
#include <iostream>
#include <optional>
#include <string>

int main()
{
	std::optional<std::string> os{ "Falling in life is inevitable--staying down is optional." };
	if (os) std::cout << (*os).size() << std::endl;
	if (os) std::cout << os->size() << std::endl;

}
/* A static variable that you define within a function is created the first time its
definition is executed. It then continues to exist until the program terminates. This
means you can carry over a value from one call of a function to the next.

*/
#include <iostream>

unsigned int nextInteger()
{
	static unsigned int count{ 0 };
	return ++count;
}

int main()
{
	/*The first time the statement starting with static executes, count is
	created and initialized to 0. Subsequent executions of the statement have
	no further effect.
	count is created and initialized only once, the first time the function
	is called. Subsequent calls simply increment count and return the resulting value.
	count survives for as long as the program is executing.*/
	std::cout << nextInteger() << std::endl;//output 1
	std::cout << nextInteger() << std::endl;//output 2
	std::cout << nextInteger() << std::endl;//output 3

}
// Overloading a function
#include <iostream>
#include <string>
#include <vector>
using std::string;
using std::vector;
// Function prototypes
double largest(const double data[], size_t count);
double largest(const vector<double>& data);
int largest(const vector<int>& data);
string largest(const vector<string>& words);
// int largest(const vector<string>& words); /* would not compile: overloaded functions must
// differ in more than just their return type! */
int main()
{
	double values[]{ 1.5, 44.6, 13.7, 21.2, 6.7 };
	vector<int> numbers{ 15, 44, 13, 21, 6, 8, 5, 2 };
	vector<double> data{ 3.5, 5, 6, -1.2, 8.7, 6.4 };
	vector<string> names{ "Charles Dickens", "Emily Bronte", "Jane Austen",
	"Henry James", "Arthur Miller" };
	std::cout << "The largest of values is " << largest(values, std::size(values)) << std::endl;
	std::cout << "The largest of numbers is " << largest(numbers) << std::endl;
	std::cout << "The largest of data is " << largest(data) << std::endl;
	std::cout << "The largest of names is " << largest(names) << std::endl;
}
// Finds the largest of an array of double values
/*Parameters of array types are different. Only the address of an array is passed in this case, so they
do not need to be reference types.*/
double largest(const double data[], size_t count)
{
	double max{ data[0] };
	for (size_t i{ 1 }; i < count; ++i)
		if (max < data[i]) max = data[i];
	return max;
}
// Finds the largest of a vector of double values
/*the parameters that accept vector<T> arguments are references. If they are not specified as references,
the vector object will be passed by value and thus copied. This could be expensive for a vector with a lot of
elements. */
double largest(const vector<double>& data)
{
	double max{ data[0] };
	for (auto value : data)
		if (max < value) max = value;
	return max;
}
// Finds the largest of a vector of int values
int largest(const vector<int>& data)
{
	int max{ data[0] };
	for (auto value : data)
		if (max < value) max = value;
	return max;
}
// Finds the largest of a vector of string objects
string largest(const vector<string>& words)
{
	string max_word{ words[0] };
	for (const auto& word : words)
		if (max_word < word) max_word = word;
	return max_word;
}
#include <iostream>

unsigned long long factorial(unsigned int n) // n < 0 impossible due to unsigned type!
{
	if (n <= 1) return 1; // 0! is normally defined as 1 as well
	return n * factorial(n - 1);
}
int main()
{
	std::cout << factorial(4) << std::endl;
	/*If this function is called with an argument value of 4, the return statement that calls the function with
a value of 3 in the expression executes. This will execute the return to call the function with an argument of
2, which will call factorial() with an argument of 1. The if expression will be true in this case, so 1 will be
returned, which will be multiplied by 2 in the next level up, and so on, until the first call returns the value
4 × 3 × 2 × 1. */
}
// Recursive version of function for x to the power n, n positive or negative
#include <iostream>
#include <iomanip>

double power(double x, int n);

int main()
{
	for (int i{ -3 }; i <= 3; ++i)     // Calculate powers of 8 from -3 to +3
		std::cout << std::setw(10) << power(8.0, i);

	std::cout << std::endl;
}

// Recursive function to calculate x to the power n
double power(double x, int n)
{
	if (n == 0)      return 1.0;
	else if (n > 0)  return x * power(x, n - 1);
	else /* n < 0 */ return 1.0 / power(x, -n);
}

#include <iostream>
#include <string>
#include <string_view>


std::string plus(std::string_view s1, std::string_view s2);

int main()
{

	const std::string s1{ "aaa" };
	const std::string s2{ "bbb" };
	const std::string s3{ plus(s1, s2) };
	std::cout << "With s1 as " << s1 << " and s2 as " << s2 << std::endl;
	std::cout << "plus(s1, s2) returns " << s3 << std::endl;

}


// Adding strings
inline std::string plus(std::string_view s1, std::string_view s2)
{
	// return s1 + s2; does not compile in this case, 
	// because there is no addition operator (+) for string_view.
	// Therefore, you first have to turn the operands into std::strings...
	return std::string{ s1 } +std::string{ s2 };
}
#include <iostream>
bool isPrime(unsigned number)
{
	// a prime number is a natural number strictly greater than 1...
	if (number <= 1) return false;

	// ...and with no positive divisors other than 1 and itself
	for (unsigned i = 2; i < std::sqrt(number); ++i)
	{
		if (number % i == 0)
		{
			return false;
		}
	}

	return true;
}

int main()
{
	int num{};
	std::cout << "Please enter a number to check whether it is prime or not: " << std::endl;
	std::cin >> num;
	std::cout << "The number you entered " << (isPrime(num) ? "is a prime." : "is not a prime.") << std::endl;
}
// Exercise 8-7 Computing Fibinacci numbers recursively. 
// Main thing to realise is that the recursion needs two base cases.
// Key is also to use unsigned values (function would fail for negative numbers)
// and not to forget about zero either (using n == 1 and n == 2 as base cases 
// would mean trouble if n == 0 is passed)
#include <iostream>

unsigned long long fib(size_t n);

int main()
{
	size_t num{};
	std::cout << "Good day, master. How many Fibonacci numbers shall I compute today?" << std::endl;
	std::cin >> num;

	for (size_t i{ 1 }; i <= num; ++i)
		std::cout << "fib(" << i << ") = " << fib(i) << '\n';
}

unsigned long long fib(size_t n)
{
	switch (n)
	{
	case 0:  return 0;
	case 1:  return 1;
	default: return fib(n - 2) + fib(n - 1);
	}
}
//divide and conquer algorithm
#include <iostream>
#include <iomanip>

long double power(double x, int n);

int main()
{
	for (int i{ 1 }; i <= 66; ++i)
	{
		for (int j{ 1 }; j <= 66; ++j)
			std::cout << power(i, j) << std::endl;
	}
}

// Recursive function to calculate x to the power n
long double power(double x, int n)
{
	if (n == 0)     return 1.0;
	else if (n < 0) return 1.0 / power(x, -n);
	else if (n % 2) return x * power(x, n - 1);     // x is odd

	// If we make it this far, x > 0 and even
	const auto y = power(x, n / 2);
	return y * y;
}

猜你喜欢

转载自blog.csdn.net/CodingIsFun/article/details/84991320